diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 47b55678..8a1467b2 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -19,7 +19,7 @@ jobs: strategy: matrix: - node-version: [16,18] + node-version: [18,20] steps: - name: "Checkout latest code" diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index e140b411..03f2f91a 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -17,7 +17,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 18 - run: npm install - run: npm run setup - run: npm link @@ -33,7 +33,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 18 registry-url: https://registry.npmjs.org/ - run: npm install - run: npm run setup diff --git a/README.md b/README.md index 2ed27460..fa3e5ffa 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![Discord](https://badgen.net/discord/members/vnmYFfySbH?icon=discord&label)](https://join.elm.land) [![Twitter](https://badgen.net/badge/icon/twitter?icon=twitter&label&color=00acee)](https://twitter.com/elmland_) [![GitHub](https://badgen.net/badge/icon/github?icon=github&label&color=4078c0)](https://www.github.com/elm-land/elm-land) -[![Elm Land: Reliable web apps for everyone](https://github.com/elm-land/elm-land/raw/main/elm-land-banner.jpg)](https://elm.land) +[![Elm Land: Reliable web apps for everyone](https://github.com/elm-land/elm-land/raw/main/docs/elm-land-banner.jpg)](https://elm.land) ## Welcome to our repo! diff --git a/docs/.vitepress/config.mjs b/docs/.vitepress/config.mjs index 49ed86ee..5ca8ff48 100644 --- a/docs/.vitepress/config.mjs +++ b/docs/.vitepress/config.mjs @@ -1,6 +1,6 @@ import { defineConfig } from 'vitepress' -const version = '0.19.5' +const version = '0.20.0' const sidebar = [ { diff --git a/docs/concepts/auth.md b/docs/concepts/auth.md index 92bed516..364edbac 100644 --- a/docs/concepts/auth.md +++ b/docs/concepts/auth.md @@ -36,7 +36,7 @@ elm-land customize auth By default, all auth-only pages redirect users to the `NotFound_` page when the application starts up. Let's edit our new `src/Auth.elm` file so it automatically passes the user to any pages that need it, but redirects to `/sign-in` if there's no user logged in. ```elm{7-8,14-28} -module Auth exposing (User, onPageLoad, viewLoadingPage) +module Auth exposing (User, onPageLoad, viewCustomPage) -- ... @@ -66,9 +66,9 @@ onPageLoad shared route = } -{-| Used whenever `Auth.Action.showLoadingPage` is returned. -} -viewLoadingPage : Shared.Model -> Route () -> View Msg -viewLoadingPage shared route = +{-| Used whenever `Auth.Action.loadCustomPage` is returned. -} +viewCustomPage : Shared.Model -> Route () -> View Msg +viewCustomPage shared route = View.none ``` diff --git a/docs/concepts/cli.md b/docs/concepts/cli.md index 82517684..5cd33925 100644 --- a/docs/concepts/cli.md +++ b/docs/concepts/cli.md @@ -15,7 +15,7 @@ detailed breakdown of the documentation you'll see in your terminal. ## elm-land init ```txt -✨ elm-land init ...... create a new project +elm-land init ...... create a new project ``` #### Description @@ -37,7 +37,7 @@ be committed to version control. ## elm-land server ```txt -🚀 elm-land server ................ run a local dev server +elm-land server ................ run a local dev server ``` #### Description @@ -65,7 +65,7 @@ Next, we're looking to add `elm-watch` to our existing Vite setup. That will com ## elm-land build ```txt -📦 elm-land build .......... build your app for production +elm-land build .......... build your app for production ``` #### Description @@ -80,7 +80,7 @@ Visit the [Deploying to production](../guide/deploying) guide to learn how to co ## elm-land generate ```txt -🪄 elm-land generate ............. generate Elm Land files +elm-land generate ............. generate Elm Land files ``` #### Description @@ -93,7 +93,7 @@ For those advanced use cases, we've added a specific `generate` command that doe ## elm-land add page ```txt -📄 elm-land add page ................ add a new page +elm-land add page ................ add a new page ``` #### Description @@ -125,7 +125,7 @@ __Because there is no command for upgrading an existing page__, we recommend usi ## elm-land add layout ```txt -🍱 elm-land add layout ........... add a new layout +elm-land add layout ........... add a new layout ``` #### Description @@ -150,7 +150,7 @@ elm-land add layout Sidebar.Header ... Creates "src/Layouts/Sidebar/Header.elm" ## elm-land customize ```txt -🔧 elm-land customize .. customize a default module +elm-land customize .. customize a default module ``` #### Description @@ -176,7 +176,7 @@ elm-land customize auth ................... handle user authentication ## elm-land routes ```txt -🔍 elm-land routes ........... list all routes in your app +elm-land routes ........... list all routes in your app ``` #### Description @@ -191,7 +191,7 @@ Here's example output of what you'd see if you ran this command in the ["Pages a ```txt - 🌈 Elm Land (v0.19.5) found 5 pages in your application + 🌈 Elm Land (v0.20.0) found 5 pages in your application ⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺ src/Pages/Home_.elm ............... http://localhost:1234/ src/Pages/SignIn.elm .............. http://localhost:1234/sign-in diff --git a/elm-land-banner.jpg b/docs/elm-land-banner.jpg similarity index 100% rename from elm-land-banner.jpg rename to docs/elm-land-banner.jpg diff --git a/docs/guide/deploying.md b/docs/guide/deploying.md index 01c62096..2969a54d 100644 --- a/docs/guide/deploying.md +++ b/docs/guide/deploying.md @@ -16,7 +16,7 @@ elm-land build ```txt -🌈 Elm Land (v0.19.5) build was successful. +🌈 Elm Land (v0.20.0) build was successful. ⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺ ``` diff --git a/docs/guide/pages-and-routes.md b/docs/guide/pages-and-routes.md index 41418d94..5bde04db 100644 --- a/docs/guide/pages-and-routes.md +++ b/docs/guide/pages-and-routes.md @@ -326,7 +326,7 @@ elm-land routes ```txt - 🌈 Elm Land (v0.19.5) found 6 pages in your application + 🌈 Elm Land (v0.20.0) found 6 pages in your application ⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺ src/Pages/Home_.elm ........................... / src/Pages/SignIn.elm .......................... /sign-in diff --git a/docs/reference/auth-action.md b/docs/reference/auth-action.md index 5db743ff..a3127ae4 100644 --- a/docs/reference/auth-action.md +++ b/docs/reference/auth-action.md @@ -65,12 +65,14 @@ Auth.Action.loadExternalUrl : String -> Auth.Action.Action ``` -### `Auth.Action.showLoadingPage` +### `Auth.Action.loadCustomPage` -Sometimes it is helpful to wait on the current page while validating if a JWT token is expired. The `showLoadingPage` function will display a static view to the user while they wait. +Sometimes it is helpful to wait on the current page while validating if a JWT token is expired. The `loadCustomPage` function will display a static view to the user while they wait. + +__Note:__ You can use `Auth.viewCustomPage` to specify what to render in each scenario #### Type definition ```elm -Auth.Action.showLoadingPage : View Never -> Auth.Action.Action +Auth.Action.loadCustomPage : Auth.Action.Action ``` \ No newline at end of file diff --git a/examples/05-user-auth/src/Auth.elm b/examples/05-user-auth/src/Auth.elm index 8db7a949..963eca8c 100644 --- a/examples/05-user-auth/src/Auth.elm +++ b/examples/05-user-auth/src/Auth.elm @@ -1,4 +1,4 @@ -module Auth exposing (User, onPageLoad, viewLoadingPage) +module Auth exposing (User, onPageLoad, viewCustomPage) import Auth.Action import Dict @@ -32,8 +32,8 @@ onPageLoad shared route = } -{-| Renders whenever `Auth.Action.showLoadingPage` is returned from `onPageLoad`. +{-| Renders whenever `Auth.Action.loadCustomPage` is returned from `onPageLoad`. -} -viewLoadingPage : Shared.Model -> Route () -> View Never -viewLoadingPage shared route = +viewCustomPage : Shared.Model -> Route () -> View Never +viewCustomPage shared route = View.fromString "Loading..." diff --git a/examples/19-tailwindcss/package.json b/examples/19-tailwindcss/package.json index 92b7396c..c6ef7647 100644 --- a/examples/19-tailwindcss/package.json +++ b/examples/19-tailwindcss/package.json @@ -13,7 +13,7 @@ "license": "BSD-3-Clause", "devDependencies": { "autoprefixer": "^10.4.16", - "elm-land": "^0.19.5", + "elm-land": "^0.20.0", "postcss": "^8.4.32", "tailwindcss": "^3.4.0" } diff --git a/examples/20-auth-error-page/.gitignore b/examples/20-auth-error-page/.gitignore new file mode 100644 index 00000000..7295c4fe --- /dev/null +++ b/examples/20-auth-error-page/.gitignore @@ -0,0 +1,7 @@ +/dist +/.elm-land +/.env +/elm-stuff +/node_modules +.DS_Store +*.pem \ No newline at end of file diff --git a/examples/20-auth-error-page/README.md b/examples/20-auth-error-page/README.md new file mode 100644 index 00000000..a7d181c0 --- /dev/null +++ b/examples/20-auth-error-page/README.md @@ -0,0 +1,16 @@ +# 20-auth-error-page +> Built with [Elm Land](https://elm.land) 🌈 + +## Local development + +```bash +# Requires Node.js v18+ (https://nodejs.org) +npx elm-land server +``` + +## Deploying to production + +Elm Land projects are most commonly deployed as static websites. + +Please visit [the "Deployment" guide](https://elm.land/guide/deploying) to learn more +about deploying your app for free using Netlify or Vercel. \ No newline at end of file diff --git a/examples/20-auth-error-page/elm-land.json b/examples/20-auth-error-page/elm-land.json new file mode 100644 index 00000000..3d9c0d7f --- /dev/null +++ b/examples/20-auth-error-page/elm-land.json @@ -0,0 +1,27 @@ +{ + "app": { + "elm": { + "development": { "debugger": true }, + "production": { "debugger": false } + }, + "env": [], + "html": { + "attributes": { + "html": { "lang": "en" }, + "head": {} + }, + "title": "Elm Land", + "meta": [ + { "charset": "UTF-8" }, + { "http-equiv": "X-UA-Compatible", "content": "IE=edge" }, + { "name": "viewport", "content": "width=device-width, initial-scale=1.0" } + ], + "link": [], + "script": [] + }, + "router": { + "useHashRouting": false + }, + "proxy": null + } +} \ No newline at end of file diff --git a/examples/20-auth-error-page/elm.json b/examples/20-auth-error-page/elm.json new file mode 100644 index 00000000..5868b5e1 --- /dev/null +++ b/examples/20-auth-error-page/elm.json @@ -0,0 +1,25 @@ +{ + "type": "application", + "source-directories": [ + "src", + ".elm-land/src" + ], + "elm-version": "0.19.1", + "dependencies": { + "direct": { + "elm/browser": "1.0.2", + "elm/core": "1.0.5", + "elm/html": "1.0.0", + "elm/json": "1.1.3", + "elm/url": "1.0.0" + }, + "indirect": { + "elm/time": "1.0.0", + "elm/virtual-dom": "1.0.3" + } + }, + "test-dependencies": { + "direct": {}, + "indirect": {} + } +} diff --git a/examples/20-auth-error-page/src/Auth.elm b/examples/20-auth-error-page/src/Auth.elm new file mode 100644 index 00000000..662cc7c8 --- /dev/null +++ b/examples/20-auth-error-page/src/Auth.elm @@ -0,0 +1,51 @@ +module Auth exposing (User, onPageLoad, viewCustomPage) + +import Auth.Action +import Dict +import Html +import Route exposing (Route) +import Route.Path +import Shared +import Shared.Model +import User +import View exposing (View) + + +type alias User = + User.User + + +{-| Called before an auth-only page is loaded. +-} +onPageLoad : Shared.Model -> Route () -> Auth.Action.Action User +onPageLoad shared route = + case shared.authStatus of + Shared.Model.NotLoggedIn -> + Auth.Action.loadCustomPage + + Shared.Model.LoggedInAs user -> + Auth.Action.loadPageWithUser user + + Shared.Model.TokenExpired -> + Auth.Action.loadCustomPage + + +{-| Renders whenever `Auth.Action.showCustomView` is returned from `onPageLoad`. +-} +viewCustomPage : Shared.Model -> Route () -> View Never +viewCustomPage shared route = + case shared.authStatus of + Shared.Model.NotLoggedIn -> + { title = "Permission denied" + , body = [ Html.text "You need to be logged in to see this!" ] + } + + Shared.Model.LoggedInAs user -> + { title = "Loading..." + , body = [] + } + + Shared.Model.TokenExpired -> + { title = "Token expired" + , body = [ Html.text "Your token expired, here's an error page!" ] + } diff --git a/examples/20-auth-error-page/src/Pages/Home_.elm b/examples/20-auth-error-page/src/Pages/Home_.elm new file mode 100644 index 00000000..b4ed887a --- /dev/null +++ b/examples/20-auth-error-page/src/Pages/Home_.elm @@ -0,0 +1,71 @@ +module Pages.Home_ exposing (Model, Msg, page) + +import Auth +import Effect exposing (Effect) +import Html +import Page exposing (Page) +import Route exposing (Route) +import Shared +import View exposing (View) + + +page : Auth.User -> Shared.Model -> Route () -> Page Model Msg +page user shared route = + Page.new + { init = init + , update = update + , subscriptions = subscriptions + , view = view + } + + + +-- INIT + + +type alias Model = + {} + + +init : () -> ( Model, Effect Msg ) +init () = + ( {} + , Effect.none + ) + + + +-- UPDATE + + +type Msg + = NoOp + + +update : Msg -> Model -> ( Model, Effect Msg ) +update msg model = + case msg of + NoOp -> + ( model + , Effect.none + ) + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.none + + + +-- VIEW + + +view : Model -> View Msg +view model = + { title = "Pages.Home_" + , body = [ Html.text "/" ] + } diff --git a/examples/20-auth-error-page/src/Shared.elm b/examples/20-auth-error-page/src/Shared.elm new file mode 100644 index 00000000..57407767 --- /dev/null +++ b/examples/20-auth-error-page/src/Shared.elm @@ -0,0 +1,75 @@ +module Shared exposing + ( Flags, decoder + , Model, Msg + , init, update, subscriptions + ) + +{-| + +@docs Flags, decoder +@docs Model, Msg +@docs init, update, subscriptions + +-} + +import Effect exposing (Effect) +import Json.Decode +import Route exposing (Route) +import Route.Path +import Shared.Model +import Shared.Msg + + + +-- FLAGS + + +type alias Flags = + {} + + +decoder : Json.Decode.Decoder Flags +decoder = + Json.Decode.succeed {} + + + +-- INIT + + +type alias Model = + Shared.Model.Model + + +init : Result Json.Decode.Error Flags -> Route () -> ( Model, Effect Msg ) +init flagsResult route = + ( { authStatus = Shared.Model.TokenExpired -- 👈 Change this to see the different custom pages + } + , Effect.none + ) + + + +-- UPDATE + + +type alias Msg = + Shared.Msg.Msg + + +update : Route () -> Msg -> Model -> ( Model, Effect Msg ) +update route msg model = + case msg of + Shared.Msg.NoOp -> + ( model + , Effect.none + ) + + + +-- SUBSCRIPTIONS + + +subscriptions : Route () -> Model -> Sub Msg +subscriptions route model = + Sub.none diff --git a/examples/20-auth-error-page/src/Shared/Model.elm b/examples/20-auth-error-page/src/Shared/Model.elm new file mode 100644 index 00000000..b9f31524 --- /dev/null +++ b/examples/20-auth-error-page/src/Shared/Model.elm @@ -0,0 +1,14 @@ +module Shared.Model exposing (AuthStatus(..), Model) + +import User exposing (User) + + +type alias Model = + { authStatus : AuthStatus + } + + +type AuthStatus + = NotLoggedIn + | LoggedInAs User + | TokenExpired diff --git a/examples/20-auth-error-page/src/Shared/Msg.elm b/examples/20-auth-error-page/src/Shared/Msg.elm new file mode 100644 index 00000000..cccb82df --- /dev/null +++ b/examples/20-auth-error-page/src/Shared/Msg.elm @@ -0,0 +1,14 @@ +module Shared.Msg exposing (Msg(..)) + +{-| -} + + +{-| Normally, this value would live in "Shared.elm" +but that would lead to a circular dependency import cycle. + +For that reason, both `Shared.Model` and `Shared.Msg` are in their +own file, so they can be imported by `Effect.elm` + +-} +type Msg + = NoOp diff --git a/examples/20-auth-error-page/src/User.elm b/examples/20-auth-error-page/src/User.elm new file mode 100644 index 00000000..eab57b57 --- /dev/null +++ b/examples/20-auth-error-page/src/User.elm @@ -0,0 +1,7 @@ +module User exposing (User) + + +type alias User = + { id : Int + , email : String + } diff --git a/projects/cli/README.md b/projects/cli/README.md index 2bfaa196..9cefe06d 100644 --- a/projects/cli/README.md +++ b/projects/cli/README.md @@ -17,17 +17,21 @@ The `elm-land` CLI comes with everything you need to create your next web applic ``` $ elm-land -🌈 Welcome to Elm Land! (v0.19.5) +🌈 Welcome to Elm Land! (v0.20.0) ⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺ - Here are the available commands: - - ✨ elm-land init ...... create a new project - 🚀 elm-land server ................ run a local dev server - 📦 elm-land build .......... build your app for production - 🪄 elm-land generate ............. generate Elm Land files - 📄 elm-land add page ................ add a new page - 🍱 elm-land add layout ........... add a new layout - 🔧 elm-land customize .. customize a default module + Commonly used commands: + + elm-land init ...... create a new project + elm-land server ................ run a local dev server + elm-land build .......... build your app for production + + Other helpful commands: + + elm-land generate ............. generate Elm Land files + elm-land add page ................ add a new page + elm-land add layout ........... add a new layout + elm-land customize .. customize a default module + elm-land routes ........... list all routes in your app Want to learn more? Visit https://elm.land/guide diff --git a/projects/cli/package-lock.json b/projects/cli/package-lock.json index 374dfa78..2c63031b 100644 --- a/projects/cli/package-lock.json +++ b/projects/cli/package-lock.json @@ -1,20 +1,20 @@ { "name": "elm-land", - "version": "0.19.5", + "version": "0.20.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "elm-land", - "version": "0.19.5", + "version": "0.20.0", "license": "ISC", "dependencies": { "chokidar": "3.5.3", "elm": "0.19.1-6", - "node-elm-compiler": "5.0.6", "terser": "5.15.1", "typescript": "4.9.3", - "vite": "4.3.9" + "vite": "5.2.8", + "vite-plugin-elm-watch": "1.3.2" }, "bin": { "elm-land": "src/index.js" @@ -72,10 +72,25 @@ "win32" ] }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", + "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@esbuild/android-arm": { - "version": "0.17.17", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.17.tgz", - "integrity": "sha512-E6VAZwN7diCa3labs0GYvhEPL2M94WLF8A+czO8hfjREXxba8Ng7nM5VxV+9ihNXIY1iQO1XxUU4P7hbqbICxg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", + "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", "cpu": [ "arm" ], @@ -88,9 +103,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.17.17", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.17.tgz", - "integrity": "sha512-jaJ5IlmaDLFPNttv0ofcwy/cfeY4bh/n705Tgh+eLObbGtQBK3EPAu+CzL95JVE4nFAliyrnEu0d32Q5foavqg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", + "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", "cpu": [ "arm64" ], @@ -103,9 +118,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.17.17", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.17.tgz", - "integrity": "sha512-446zpfJ3nioMC7ASvJB1pszHVskkw4u/9Eu8s5yvvsSDTzYh4p4ZIRj0DznSl3FBF0Z/mZfrKXTtt0QCoFmoHA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", + "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", "cpu": [ "x64" ], @@ -118,9 +133,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.17.17", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.17.tgz", - "integrity": "sha512-m/gwyiBwH3jqfUabtq3GH31otL/0sE0l34XKpSIqR7NjQ/XHQ3lpmQHLHbG8AHTGCw8Ao059GvV08MS0bhFIJQ==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", + "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", "cpu": [ "arm64" ], @@ -133,9 +148,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.17.17", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.17.tgz", - "integrity": "sha512-4utIrsX9IykrqYaXR8ob9Ha2hAY2qLc6ohJ8c0CN1DR8yWeMrTgYFjgdeQ9LIoTOfLetXjuCu5TRPHT9yKYJVg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", + "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", "cpu": [ "x64" ], @@ -148,9 +163,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.17.17", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.17.tgz", - "integrity": "sha512-4PxjQII/9ppOrpEwzQ1b0pXCsFLqy77i0GaHodrmzH9zq2/NEhHMAMJkJ635Ns4fyJPFOlHMz4AsklIyRqFZWA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", + "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", "cpu": [ "arm64" ], @@ -163,9 +178,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.17.17", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.17.tgz", - "integrity": "sha512-lQRS+4sW5S3P1sv0z2Ym807qMDfkmdhUYX30GRBURtLTrJOPDpoU0kI6pVz1hz3U0+YQ0tXGS9YWveQjUewAJw==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", + "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", "cpu": [ "x64" ], @@ -178,9 +193,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.17.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.17.tgz", - "integrity": "sha512-biDs7bjGdOdcmIk6xU426VgdRUpGg39Yz6sT9Xp23aq+IEHDb/u5cbmu/pAANpDB4rZpY/2USPhCA+w9t3roQg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", + "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", "cpu": [ "arm" ], @@ -193,9 +208,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.17.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.17.tgz", - "integrity": "sha512-2+pwLx0whKY1/Vqt8lyzStyda1v0qjJ5INWIe+d8+1onqQxHLLi3yr5bAa4gvbzhZqBztifYEu8hh1La5+7sUw==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", + "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", "cpu": [ "arm64" ], @@ -208,9 +223,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.17.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.17.tgz", - "integrity": "sha512-IBTTv8X60dYo6P2t23sSUYym8fGfMAiuv7PzJ+0LcdAndZRzvke+wTVxJeCq4WgjppkOpndL04gMZIFvwoU34Q==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", + "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", "cpu": [ "ia32" ], @@ -223,9 +238,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.17.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.17.tgz", - "integrity": "sha512-WVMBtcDpATjaGfWfp6u9dANIqmU9r37SY8wgAivuKmgKHE+bWSuv0qXEFt/p3qXQYxJIGXQQv6hHcm7iWhWjiw==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", + "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", "cpu": [ "loong64" ], @@ -238,9 +253,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.17.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.17.tgz", - "integrity": "sha512-2kYCGh8589ZYnY031FgMLy0kmE4VoGdvfJkxLdxP4HJvWNXpyLhjOvxVsYjYZ6awqY4bgLR9tpdYyStgZZhi2A==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", + "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", "cpu": [ "mips64el" ], @@ -253,9 +268,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.17.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.17.tgz", - "integrity": "sha512-KIdG5jdAEeAKogfyMTcszRxy3OPbZhq0PPsW4iKKcdlbk3YE4miKznxV2YOSmiK/hfOZ+lqHri3v8eecT2ATwQ==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", + "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", "cpu": [ "ppc64" ], @@ -268,9 +283,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.17.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.17.tgz", - "integrity": "sha512-Cj6uWLBR5LWhcD/2Lkfg2NrkVsNb2sFM5aVEfumKB2vYetkA/9Uyc1jVoxLZ0a38sUhFk4JOVKH0aVdPbjZQeA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", + "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", "cpu": [ "riscv64" ], @@ -283,9 +298,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.17.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.17.tgz", - "integrity": "sha512-lK+SffWIr0XsFf7E0srBjhpkdFVJf3HEgXCwzkm69kNbRar8MhezFpkIwpk0qo2IOQL4JE4mJPJI8AbRPLbuOQ==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", + "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", "cpu": [ "s390x" ], @@ -298,9 +313,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.17.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.17.tgz", - "integrity": "sha512-XcSGTQcWFQS2jx3lZtQi7cQmDYLrpLRyz1Ns1DzZCtn898cWfm5Icx/DEWNcTU+T+tyPV89RQtDnI7qL2PObPg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", + "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", "cpu": [ "x64" ], @@ -313,9 +328,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.17.17", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.17.tgz", - "integrity": "sha512-RNLCDmLP5kCWAJR+ItLM3cHxzXRTe4N00TQyQiimq+lyqVqZWGPAvcyfUBM0isE79eEZhIuGN09rAz8EL5KdLA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", + "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", "cpu": [ "x64" ], @@ -328,9 +343,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.17.17", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.17.tgz", - "integrity": "sha512-PAXswI5+cQq3Pann7FNdcpSUrhrql3wKjj3gVkmuz6OHhqqYxKvi6GgRBoaHjaG22HV/ZZEgF9TlS+9ftHVigA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", + "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", "cpu": [ "x64" ], @@ -343,9 +358,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.17.17", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.17.tgz", - "integrity": "sha512-V63egsWKnx/4V0FMYkr9NXWrKTB5qFftKGKuZKFIrAkO/7EWLFnbBZNM1CvJ6Sis+XBdPws2YQSHF1Gqf1oj/Q==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", + "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", "cpu": [ "x64" ], @@ -358,9 +373,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.17.17", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.17.tgz", - "integrity": "sha512-YtUXLdVnd6YBSYlZODjWzH+KzbaubV0YVd6UxSfoFfa5PtNJNaW+1i+Hcmjpg2nEe0YXUCNF5bkKy1NnBv1y7Q==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", + "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", "cpu": [ "arm64" ], @@ -373,9 +388,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.17.17", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.17.tgz", - "integrity": "sha512-yczSLRbDdReCO74Yfc5tKG0izzm+lPMYyO1fFTcn0QNwnKmc3K+HdxZWLGKg4pZVte7XVgcFku7TIZNbWEJdeQ==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", + "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", "cpu": [ "ia32" ], @@ -388,9 +403,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.17.17", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.17.tgz", - "integrity": "sha512-FNZw7H3aqhF9OyRQbDDnzUApDXfC1N6fgBhkqEO2jvYCJ+DxMTfZVqg3AX0R1khg1wHTBRD5SdcibSJ+XF6bFg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", + "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", "cpu": [ "x64" ], @@ -403,61 +418,222 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz", - "integrity": "sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dependencies": { - "@jridgewell/set-array": "^1.0.0", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz", - "integrity": "sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.1.tgz", - "integrity": "sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.13", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz", - "integrity": "sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==" + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz", - "integrity": "sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", + "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz", + "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz", + "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz", + "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz", + "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz", + "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz", + "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz", + "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz", + "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz", + "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz", + "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz", + "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz", + "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + }, "node_modules/acorn": { - "version": "8.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", - "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "bin": { "acorn": "bin/acorn" }, @@ -498,11 +674,6 @@ "node": ">= 8" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, "node_modules/bats": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/bats/-/bats-1.7.0.tgz", @@ -520,15 +691,6 @@ "node": ">=8" } }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/braces": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", @@ -629,37 +791,6 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } - }, - "node_modules/cross-spawn/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, "node_modules/decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", @@ -694,9 +825,9 @@ "dev": true }, "node_modules/esbuild": { - "version": "0.17.17", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.17.tgz", - "integrity": "sha512-/jUywtAymR8jR4qsa2RujlAF7Krpt5VWi72Q2yuLD4e/hvtNcFQ0I1j8m/bxq238pf3/0KO5yuXNpuLx8BE1KA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", + "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" @@ -705,28 +836,29 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/android-arm": "0.17.17", - "@esbuild/android-arm64": "0.17.17", - "@esbuild/android-x64": "0.17.17", - "@esbuild/darwin-arm64": "0.17.17", - "@esbuild/darwin-x64": "0.17.17", - "@esbuild/freebsd-arm64": "0.17.17", - "@esbuild/freebsd-x64": "0.17.17", - "@esbuild/linux-arm": "0.17.17", - "@esbuild/linux-arm64": "0.17.17", - "@esbuild/linux-ia32": "0.17.17", - "@esbuild/linux-loong64": "0.17.17", - "@esbuild/linux-mips64el": "0.17.17", - "@esbuild/linux-ppc64": "0.17.17", - "@esbuild/linux-riscv64": "0.17.17", - "@esbuild/linux-s390x": "0.17.17", - "@esbuild/linux-x64": "0.17.17", - "@esbuild/netbsd-x64": "0.17.17", - "@esbuild/openbsd-x64": "0.17.17", - "@esbuild/sunos-x64": "0.17.17", - "@esbuild/win32-arm64": "0.17.17", - "@esbuild/win32-ia32": "0.17.17", - "@esbuild/win32-x64": "0.17.17" + "@esbuild/aix-ppc64": "0.20.2", + "@esbuild/android-arm": "0.20.2", + "@esbuild/android-arm64": "0.20.2", + "@esbuild/android-x64": "0.20.2", + "@esbuild/darwin-arm64": "0.20.2", + "@esbuild/darwin-x64": "0.20.2", + "@esbuild/freebsd-arm64": "0.20.2", + "@esbuild/freebsd-x64": "0.20.2", + "@esbuild/linux-arm": "0.20.2", + "@esbuild/linux-arm64": "0.20.2", + "@esbuild/linux-ia32": "0.20.2", + "@esbuild/linux-loong64": "0.20.2", + "@esbuild/linux-mips64el": "0.20.2", + "@esbuild/linux-ppc64": "0.20.2", + "@esbuild/linux-riscv64": "0.20.2", + "@esbuild/linux-s390x": "0.20.2", + "@esbuild/linux-x64": "0.20.2", + "@esbuild/netbsd-x64": "0.20.2", + "@esbuild/openbsd-x64": "0.20.2", + "@esbuild/sunos-x64": "0.20.2", + "@esbuild/win32-arm64": "0.20.2", + "@esbuild/win32-ia32": "0.20.2", + "@esbuild/win32-x64": "0.20.2" } }, "node_modules/fill-range": { @@ -740,21 +872,6 @@ "node": ">=8" } }, - "node_modules/find-elm-dependencies": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/find-elm-dependencies/-/find-elm-dependencies-2.0.4.tgz", - "integrity": "sha512-x/4w4fVmlD2X4PD9oQ+yh9EyaQef6OtEULdMGBTuWx0Nkppvo2Z/bAiQioW2n+GdRYKypME2b9OmYTw5tw5qDg==", - "dependencies": { - "firstline": "^1.2.0", - "lodash": "^4.17.19" - }, - "bin": { - "find-elm-dependencies": "bin/cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/find-up": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", @@ -767,23 +884,10 @@ "node": ">=6" } }, - "node_modules/firstline": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/firstline/-/firstline-1.3.1.tgz", - "integrity": "sha512-ycwgqtoxujz1dm0kjkBFOPQMESxB9uKc/PlD951dQDIG+tBXRpYZC2UmJb0gDxopQ1ZX6oyRQN3goRczYu7Deg==", - "engines": { - "node": ">=6.4.0" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "hasInstallScript": true, "optional": true, "os": [ @@ -802,25 +906,6 @@ "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -832,20 +917,6 @@ "node": ">= 6" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -898,6 +969,15 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, + "node_modules/launch-editor": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.1.tgz", + "integrity": "sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw==", + "dependencies": { + "picocolors": "^1.0.0", + "shell-quote": "^1.8.1" + } + }, "node_modules/locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", @@ -911,11 +991,6 @@ "node": ">=6" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -928,33 +1003,6 @@ "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", "dev": true }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" - }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -972,25 +1020,6 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" - }, - "node_modules/node-elm-compiler": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/node-elm-compiler/-/node-elm-compiler-5.0.6.tgz", - "integrity": "sha512-DWTRQR8b54rvschcZRREdsz7K84lnS8A6YJu8du3QLQ8f204SJbyTaA6NzYYbfUG97OTRKRv/0KZl82cTfpLhA==", - "dependencies": { - "cross-spawn": "6.0.5", - "find-elm-dependencies": "^2.0.4", - "lodash": "^4.17.19", - "temp": "^0.9.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -999,14 +1028,6 @@ "node": ">=0.10.0" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dependencies": { - "wrappy": "1" - } - }, "node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -1052,22 +1073,6 @@ "node": ">=4" } }, - "node_modules/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=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "engines": { - "node": ">=4" - } - }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -1085,9 +1090,9 @@ } }, "node_modules/postcss": { - "version": "8.4.33", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", - "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==", + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", "funding": [ { "type": "opencollective", @@ -1105,7 +1110,7 @@ "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "source-map-js": "^1.2.0" }, "engines": { "node": "^10 || ^12 || >=14" @@ -1137,63 +1142,49 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, - "node_modules/rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "node_modules/rollup": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", + "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==", "dependencies": { - "glob": "^7.1.3" + "@types/estree": "1.0.5" }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/rollup": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.23.0.tgz", - "integrity": "sha512-h31UlwEi7FHihLe1zbk+3Q7z1k/84rb9BSwmBSr/XjOCEaBJ2YyedQDuM0t/kfOS0IxM+vk1/zI9XxYj9V+NJQ==", "bin": { "rollup": "dist/bin/rollup" }, "engines": { - "node": ">=14.18.0", + "node": ">=18.0.0", "npm": ">=8.0.0" }, "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.13.0", + "@rollup/rollup-android-arm64": "4.13.0", + "@rollup/rollup-darwin-arm64": "4.13.0", + "@rollup/rollup-darwin-x64": "4.13.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.13.0", + "@rollup/rollup-linux-arm64-gnu": "4.13.0", + "@rollup/rollup-linux-arm64-musl": "4.13.0", + "@rollup/rollup-linux-riscv64-gnu": "4.13.0", + "@rollup/rollup-linux-x64-gnu": "4.13.0", + "@rollup/rollup-linux-x64-musl": "4.13.0", + "@rollup/rollup-win32-arm64-msvc": "4.13.0", + "@rollup/rollup-win32-ia32-msvc": "4.13.0", + "@rollup/rollup-win32-x64-msvc": "4.13.0", "fsevents": "~2.3.2" } }, - "node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "bin": { - "semver": "bin/semver" - } - }, "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, - "node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "engines": { - "node": ">=0.10.0" + "node_modules/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/source-map": { @@ -1205,9 +1196,9 @@ } }, "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", "engines": { "node": ">=0.10.0" } @@ -1247,18 +1238,6 @@ "node": ">=6" } }, - "node_modules/temp": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz", - "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", - "dependencies": { - "mkdirp": "^0.5.1", - "rimraf": "~2.6.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/terser": { "version": "5.15.1", "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz", @@ -1276,6 +1255,11 @@ "node": ">=10" } }, + "node_modules/tiny-decoders": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/tiny-decoders/-/tiny-decoders-7.0.1.tgz", + "integrity": "sha512-P1LaHTLASl/lCrdtwgAAVwxt4bEAPmxpf9HMQrlCkAseaT8oH8oxm8ndy4nx5rLTcL5U/Qxp1a+FDoQfS/ZgQQ==" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -1300,26 +1284,30 @@ } }, "node_modules/vite": { - "version": "4.3.9", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz", - "integrity": "sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==", + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.8.tgz", + "integrity": "sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==", "dependencies": { - "esbuild": "^0.17.5", - "postcss": "^8.4.23", - "rollup": "^3.21.0" + "esbuild": "^0.20.1", + "postcss": "^8.4.38", + "rollup": "^4.13.0" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" }, "optionalDependencies": { - "fsevents": "~2.3.2" + "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": ">= 14", + "@types/node": "^18.0.0 || >=20.0.0", "less": "*", + "lightningcss": "^1.21.0", "sass": "*", "stylus": "*", "sugarss": "*", @@ -1332,6 +1320,9 @@ "less": { "optional": true }, + "lightningcss": { + "optional": true + }, "sass": { "optional": true }, @@ -1346,6 +1337,89 @@ } } }, + "node_modules/vite-plugin-elm-watch": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/vite-plugin-elm-watch/-/vite-plugin-elm-watch-1.3.2.tgz", + "integrity": "sha512-YLhx0h0aPJGl3Od7TK+++gakrIiG2Ct7lcs1/oOrVEh1duYDInV+x7Ps35kv7qIPlynerwn/ezB48/9gYPd35w==", + "dependencies": { + "cross-spawn": "7.0.3", + "elm": "0.19.1-6", + "launch-editor": "2.6.1", + "terser": "5.26.0", + "tiny-decoders": "7.0.1" + } + }, + "node_modules/vite-plugin-elm-watch/node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/vite-plugin-elm-watch/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/vite-plugin-elm-watch/node_modules/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==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/vite-plugin-elm-watch/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/vite-plugin-elm-watch/node_modules/terser": { + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.26.0.tgz", + "integrity": "sha512-dytTGoE2oHgbNV9nTzgBEPaqAWvcJNl66VZ0BkJqlvp71IjO8CxdBx/ykCNb47cLnCmCvRZ6ZR0tLkqvZCdVBQ==", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/which-module": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", @@ -1366,11 +1440,6 @@ "node": ">=6" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, "node_modules/y18n": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", diff --git a/projects/cli/package.json b/projects/cli/package.json index f3fbcce0..49e4ae06 100644 --- a/projects/cli/package.json +++ b/projects/cli/package.json @@ -1,8 +1,9 @@ { "name": "elm-land", - "version": "0.19.5", + "version": "0.20.0", "description": "Reliable web apps for everyone", "main": "index.js", + "type": "module", "types": "./index.d.ts", "bin": { "elm-land": "src/index.js" @@ -11,8 +12,8 @@ "start": "npm install && npm run setup && npm run dev", "setup": "npm run build", "build": "npm run build:codegen-worker && npm run build:validate-worker", - "build:codegen-worker": "(cd src/codegen && elm make src/Worker.elm --optimize --output=../../dist/codegen-worker.js)", - "build:validate-worker": "(cd src/validate && elm make src/Worker.elm --optimize --output=../../dist/validate-worker.js)", + "build:codegen-worker": "(cd src/codegen && elm make src/Worker.elm --optimize --output=../../dist/codegen-worker.js && mv ../../dist/codegen-worker.js ../../dist/codegen-worker.cjs)", + "build:validate-worker": "(cd src/validate && elm make src/Worker.elm --optimize --output=../../dist/validate-worker.js && mv ../../dist/validate-worker.js ../../dist/validate-worker.cjs)", "dev": "npm run dev:codegen-worker & npm run dev:validate-worker", "dev:codegen-worker": "chokidar src/codegen/src -c \"npm run build:codegen-worker\" --initial", "dev:validate-worker": "chokidar src/validate/src -c \"npm run build:validate-worker\" --initial", @@ -40,9 +41,9 @@ "dependencies": { "chokidar": "3.5.3", "elm": "0.19.1-6", - "node-elm-compiler": "5.0.6", "terser": "5.15.1", "typescript": "4.9.3", - "vite": "4.3.9" + "vite": "5.2.8", + "vite-plugin-elm-watch": "1.3.2" } } diff --git a/projects/cli/src/cli.js b/projects/cli/src/cli.js index be9b5d64..ea84a876 100644 --- a/projects/cli/src/cli.js +++ b/projects/cli/src/cli.js @@ -1,25 +1,34 @@ -const { Init } = require('./commands/init') -const { Add } = require('./commands/add') -const { Server } = require('./commands/server') -const { Generate } = require('./commands/generate') -const { Build } = require('./commands/build') -const { Customize } = require('./commands/customize') -const { Routes } = require('./commands/routes') -const { Utils, Terminal } = require('./commands/_utils') +import { Init } from './commands/init.js' +import { Add } from './commands/add.js' +import { Server } from './commands/server.js' +import { Generate } from './commands/generate.js' +import { Build } from './commands/build.js' +import { Customize } from './commands/customize.js' +import { Routes } from './commands/routes.js' +import { Utils, Terminal } from './commands/_utils.js' +import fs from 'fs/promises' +import path from 'path' +import url from 'url' -let { version } = require('../package.json') +let __dirname = path.dirname(url.fileURLToPath(import.meta.url)) +let packageJsonContents = await fs.readFile(path.join(__dirname, '..', 'package.json'), { encoding: 'utf-8' }) +let packageJson = JSON.parse(packageJsonContents) +let version = packageJson.version let subcommandList = [ - ` Here are the available commands:`, + ` Commonly used commands:`, ``, - ` ✨ elm-land ${Terminal.pink('init ')} ...... create a new project`, - ` 🚀 elm-land ${Terminal.pink('server')} ................ run a local dev server`, - ` 📦 elm-land ${Terminal.pink('build')} .......... build your app for production`, - ` 🪄 elm-land ${Terminal.pink('generate')} ............. generate Elm Land files`, - ` 📄 elm-land ${Terminal.pink('add page ')} ................ add a new page`, - ` 🍱 elm-land ${Terminal.pink('add layout ')} ........... add a new layout`, - ` 🔧 elm-land ${Terminal.pink('customize ')} .. customize a default module`, - ` 🔍 elm-land ${Terminal.pink('routes')} ........... list all routes in your app`, + ` elm-land ${Terminal.pink('init ')} ...... create a new project`, + ` elm-land ${Terminal.pink('server')} ................ run a local dev server`, + ` elm-land ${Terminal.pink('build')} .......... build your app for production`, + '', + ' Other helpful commands:', + '', + ` elm-land ${Terminal.pink('generate')} ............. generate Elm Land files`, + ` elm-land ${Terminal.pink('add page ')} ................ add a new page`, + ` elm-land ${Terminal.pink('add layout ')} ........... add a new layout`, + ` elm-land ${Terminal.pink('customize ')} .. customize a default module`, + ` elm-land ${Terminal.pink('routes')} ........... list all routes in your app`, ] @@ -61,7 +70,7 @@ let run = async (commandFromCli) => { if (isHelpFlag(args[0])) { return Add.printHelpInfo() } else { - return Add.run({ arguments: args }) + return Add.run({ args }) } }, 'server': (args = []) => { @@ -151,6 +160,4 @@ const isHelpFlag = (str) => { return ['-h', '--help'].includes(str) } -module.exports = { - Cli: { run } -} \ No newline at end of file +export const Cli = { run } \ No newline at end of file diff --git a/projects/cli/src/codegen.js b/projects/cli/src/codegen.js index 8ec19306..ba444117 100644 --- a/projects/cli/src/codegen.js +++ b/projects/cli/src/codegen.js @@ -1,8 +1,8 @@ -const path = require('path') -const { Files } = require('./files') +import { join } from 'path' +import { Files } from './files.js' let generateElmLandFiles = async ({ pages, layouts, router }) => { - let { Elm } = require('../dist/codegen-worker.js') + let { Elm } = (await import('../dist/codegen-worker.cjs')).default let newFiles = await new Promise((resolve, reject) => { // Insert not found page if it hasn't been customized yet @@ -23,9 +23,9 @@ let generateElmLandFiles = async ({ pages, layouts, router }) => { } let addNewPage = async ({ kind, url, filepath }) => { - let { Elm } = require('../dist/codegen-worker.js') + let { Elm } = (await import('../dist/codegen-worker.cjs')).default - let hasViewBeenCustomized = await Files.exists(path.join(process.cwd(), 'src', 'View.elm')) + let hasViewBeenCustomized = await Files.exists(join(process.cwd(), 'src', 'View.elm')) let newFiles = await new Promise((resolve, reject) => { let app = Elm.Worker.init({ @@ -46,7 +46,7 @@ let addNewPage = async ({ kind, url, filepath }) => { } let addNewLayout = async ({ moduleSegments }) => { - let { Elm } = require('../dist/codegen-worker.js') + let { Elm } = (await import('../dist/codegen-worker.cjs')).default let newFiles = await new Promise((resolve, reject) => { let app = Elm.Worker.init({ @@ -61,12 +61,10 @@ let addNewLayout = async ({ moduleSegments }) => { return newFiles } -module.exports = { - Codegen: { - generateElmLandFiles, - addNewPage, - addNewLayout - } +export const Codegen = { + generateElmLandFiles, + addNewPage, + addNewLayout } // Not found page diff --git a/projects/cli/src/codegen/src/Commands/Generate.elm b/projects/cli/src/codegen/src/Commands/Generate.elm index f9feec76..8ddad01b 100644 --- a/projects/cli/src/codegen/src/Commands/Generate.elm +++ b/projects/cli/src/codegen/src/Commands/Generate.elm @@ -351,15 +351,20 @@ mainElmModule data = , { name = "UrlRequested" , arguments = [ CodeGen.Argument.new "(Browser.External url)" ] , expression = - CodeGen.Expression.multilineTuple - [ CodeGen.Expression.value "model" - , CodeGen.Expression.function - { name = "Browser.Navigation.load" - , arguments = - [ CodeGen.Expression.value "url" + CodeGen.Expression.ifElse + { condition = CodeGen.Expression.value "String.isEmpty (String.trim url)" + , ifBranch = CodeGen.Expression.value "( model, Cmd.none )" + , elseBranch = + CodeGen.Expression.multilineTuple + [ CodeGen.Expression.value "model" + , CodeGen.Expression.function + { name = "Browser.Navigation.load" + , arguments = + [ CodeGen.Expression.value "url" + ] + } ] - } - ] + } } , { name = "UrlChanged" , arguments = [ CodeGen.Argument.new "url" ] @@ -1250,7 +1255,7 @@ hasActionTypeChangedDeclaration = , arguments = [] , expression = CodeGen.Expression.value "False" } - , { name = "( Auth.Action.ShowLoadingPage _, Auth.Action.ShowLoadingPage _ )" + , { name = "( Auth.Action.LoadCustomPage, Auth.Action.LoadCustomPage )" , arguments = [] , expression = CodeGen.Expression.value "False" } @@ -1368,8 +1373,8 @@ runWhenAuthenticatedWithLayoutDeclaration = , arguments = [ CodeGen.Argument.new "user" ] , expression = CodeGen.Expression.value "toRecord user" } - , { name = "Auth.Action.ShowLoadingPage" - , arguments = [ CodeGen.Argument.new "loadingView" ] + , { name = "Auth.Action.LoadCustomPage" + , arguments = [] , expression = wrapInPageLayout (CodeGen.Expression.multilineTuple @@ -1565,7 +1570,7 @@ toViewPageCaseExpression pages = conditionallyWrapInAuthView page expression = if PageFile.isAuthProtectedPage page then CodeGen.Expression.multilineFunction - { name = "Auth.Action.view" + { name = "Auth.Action.view (View.map never (Auth.viewCustomPage model.shared (Route.fromUrl () model.url)))" , arguments = [ CodeGen.Expression.multilineLambda { arguments = [ CodeGen.Argument.new "user" ] @@ -1600,7 +1605,7 @@ toViewPageCaseExpression pages = , arguments = [] , expression = CodeGen.Expression.pipeline - [ CodeGen.Expression.value "Auth.viewLoadingPage model.shared (Route.fromUrl () model.url)" + [ CodeGen.Expression.value "Auth.viewCustomPage model.shared (Route.fromUrl () model.url)" , CodeGen.Expression.value "View.map never" ] } diff --git a/projects/cli/src/commands/_utils.js b/projects/cli/src/commands/_utils.js index 4f81baa2..6df8c157 100644 --- a/projects/cli/src/commands/_utils.js +++ b/projects/cli/src/commands/_utils.js @@ -1,12 +1,19 @@ -let { version } = require('../../package.json') +import fs from 'fs/promises' +import path from 'path' +import url from 'url' -const Terminal = { - bold: (str) => '\033[1m' + str + '\033[0m', - dim: (str) => '\033[2m' + str + '\033[0m', - red: (str) => '\033[31m' + str + '\033[0m', - green: (str) => '\033[32m' + str + '\033[0m', - pink: (str) => '\033[35m' + str + '\033[0m', - cyan: (str) => '\033[36m' + str + '\033[0m', +let __dirname = path.dirname(url.fileURLToPath(import.meta.url)) +let packageJsonContents = await fs.readFile(path.join(__dirname, '..', '..', 'package.json'), { encoding: 'utf-8' }) +let packageJson = JSON.parse(packageJsonContents) +let version = packageJson.version + +export const Terminal = { + bold: (str) => '\x1b[1m' + str + '\x1b[0m', + dim: (str) => '\x1b[2m' + str + '\x1b[0m', + red: (str) => '\x1b[31m' + str + '\x1b[0m', + green: (str) => '\x1b[32m' + str + '\x1b[0m', + pink: (str) => '\x1b[35m' + str + '\x1b[0m', + cyan: (str) => '\x1b[36m' + str + '\x1b[0m', } const stripAnsi = (str) => @@ -78,55 +85,51 @@ let couldntFindTypeScriptBinary = (filepath) => [ let customizableFiles = { 'shared': { filepaths: [ - {src: 'Shared.elm', target: 'Shared.elm'}, - {src: 'Shared/Model.elm', target: 'Shared/Model.elm'}, - {src: 'Shared/Msg.elm', target: 'Shared/Msg.elm'} + { src: 'Shared.elm', target: 'Shared.elm' }, + { src: 'Shared/Model.elm', target: 'Shared/Model.elm' }, + { src: 'Shared/Msg.elm', target: 'Shared/Msg.elm' } ], description: '.................... share data across pages' }, 'not-found': { - filepaths: [{src: 'Pages/NotFound_.elm', target: 'Pages/NotFound_.elm'}], + filepaths: [{ src: 'Pages/NotFound_.elm', target: 'Pages/NotFound_.elm' }], description: '... the 404 page shown for unknown routes' }, 'view': { - filepaths: [{src: 'View.elm', target: 'View.elm'}], + filepaths: [{ src: 'View.elm', target: 'View.elm' }], description: '......... use whatever Elm UI package you like' }, 'view:elm-ui': { - filepaths: [{src: 'ViewElmUi.elm', target: 'View.elm'}], + filepaths: [{ src: 'ViewElmUi.elm', target: 'View.elm' }], description: '............................ use Elm UI' }, 'view:elm-css': { - filepaths: [{src: 'ViewElmCss.elm', target: 'View.elm'}], + filepaths: [{ src: 'ViewElmCss.elm', target: 'View.elm' }], description: '.......................... use Elm CSS' }, 'effect': { - filepaths: [{src: 'Effect.elm', target: 'Effect.elm'}], + filepaths: [{ src: 'Effect.elm', target: 'Effect.elm' }], description: '............. send custom effects from pages' }, 'auth': { - filepaths: [{src: 'Auth.elm', target: 'Auth.elm'}], + filepaths: [{ src: 'Auth.elm', target: 'Auth.elm' }], description: '................... handle user authentication' }, 'js': { - filepaths: [{src: 'interop.js', target: 'interop.js'}], + filepaths: [{ src: 'interop.js', target: 'interop.js' }], description: '......... work with JavaScript, flags, and ports' }, 'ts': { - filepaths: [{src: 'interop.ts', target: 'interop.ts'}], + filepaths: [{ src: 'interop.ts', target: 'interop.ts' }], description: '......... work with TypeScript, flags, and ports' }, } -module.exports = { - Terminal, - Utils: { - intro, - didNotRecognizeCommand, - notInElmLandProject, - foundTypeScriptErrors, - couldntFindTypeScriptBinary, - customizableFiles - } -} - +export const Utils = { + intro, + didNotRecognizeCommand, + notInElmLandProject, + foundTypeScriptErrors, + couldntFindTypeScriptBinary, + customizableFiles +} \ No newline at end of file diff --git a/projects/cli/src/commands/add.js b/projects/cli/src/commands/add.js index c758bde9..77ed1cf4 100644 --- a/projects/cli/src/commands/add.js +++ b/projects/cli/src/commands/add.js @@ -1,7 +1,7 @@ -const { Files } = require("../files") -const { Utils, Terminal } = require("./_utils") -const path = require('path') -const { Codegen } = require("../codegen") +import { Files } from "../files.js" +import { Utils, Terminal } from "./_utils.js" +import { join } from 'path' +import { Codegen } from "../codegen.js" let addNewLayout = () => async ([name]) => { if (name === '-h' || name === '--help') { @@ -36,7 +36,7 @@ let addNewLayout = () => async ([name]) => { } let inFolderWithElmLandJson = - await Files.exists(path.join(process.cwd(), 'elm-land.json')) + await Files.exists(join(process.cwd(), 'elm-land.json')) if (!inFolderWithElmLandJson) { return Promise.reject(Utils.notInElmLandProject) @@ -158,7 +158,7 @@ let addNewPage = (kind) => async ([originalUrl]) => { ...pageAddExamples ].join('\n')) } - let inFolderWithElmLandJson = await Files.exists(path.join(process.cwd(), 'elm-land.json')) + let inFolderWithElmLandJson = await Files.exists(join(process.cwd(), 'elm-land.json')) if (!inFolderWithElmLandJson) { return Promise.reject(Utils.notInElmLandProject) @@ -250,8 +250,8 @@ let printHelpInfo = () => { } } -let run = async ({ arguments }) => { - let [subCommand, ...otherArgs] = arguments +let run = async ({ args }) => { + let [subCommand, ...otherArgs] = args let subCommandHandlers = { 'page': addNewPage('new'), 'page:view': addNewPage('static'), @@ -273,10 +273,8 @@ let run = async ({ arguments }) => { } } -module.exports = { - Add: { - run, printHelpInfo - } +export const Add = { + run, printHelpInfo } // Return true if the string starts with a lowercase letter between a-z, diff --git a/projects/cli/src/commands/build.js b/projects/cli/src/commands/build.js index 8a1efbbe..002afaba 100644 --- a/projects/cli/src/commands/build.js +++ b/projects/cli/src/commands/build.js @@ -1,5 +1,5 @@ -const { Files } = require("../files") -const { Utils, Terminal } = require("./_utils") +import { Files } from "../files.js" +import { Utils, Terminal } from "./_utils.js" let printHelpInfo = () => { return { @@ -48,8 +48,6 @@ let run = async () => { } } -module.exports = { - Build: { - run, printHelpInfo - } +export const Build = { + run, printHelpInfo } \ No newline at end of file diff --git a/projects/cli/src/commands/customize.js b/projects/cli/src/commands/customize.js index 702a29ff..caadd7de 100644 --- a/projects/cli/src/commands/customize.js +++ b/projects/cli/src/commands/customize.js @@ -1,5 +1,5 @@ -const { Files } = require("../files") -const { Utils, Terminal } = require("./_utils") +import { Files } from "../files.js" +import { Utils, Terminal } from "./_utils.js" let printHelpInfo = () => { return { @@ -62,7 +62,7 @@ let run = async ({ moduleName } = {}) => { let count = obj.filepaths.length let countWithUnits = count === 1 ? `${count} new file` : `${count} new files` - let pathsToNewFiles = obj.filepaths.map(({target}) => Terminal.cyan(` ./src/${target}`)) + let pathsToNewFiles = obj.filepaths.map(({ target }) => Terminal.cyan(` ./src/${target}`)) let helpMessage = count === 1 ? ` If this was a mistake, you can delete that file\n to safely restore the original version.` @@ -84,8 +84,6 @@ let run = async ({ moduleName } = {}) => { } } -module.exports = { - Customize: { - run, printHelpInfo - } +export const Customize = { + run, printHelpInfo } diff --git a/projects/cli/src/commands/generate.js b/projects/cli/src/commands/generate.js index fc2a21e8..19a17120 100644 --- a/projects/cli/src/commands/generate.js +++ b/projects/cli/src/commands/generate.js @@ -1,5 +1,5 @@ -const { Files } = require("../files") -const { Utils, Terminal } = require("./_utils") +import { Files } from "../files.js" +import { Utils, Terminal } from "./_utils.js" let printHelpInfo = () => { return { @@ -47,8 +47,6 @@ let run = async () => { } } -module.exports = { - Generate: { - run, printHelpInfo - } +export const Generate = { + run, printHelpInfo } \ No newline at end of file diff --git a/projects/cli/src/commands/init.js b/projects/cli/src/commands/init.js index 6dde7d75..2a5be988 100644 --- a/projects/cli/src/commands/init.js +++ b/projects/cli/src/commands/init.js @@ -1,6 +1,6 @@ -const path = require('path') -const { Files } = require("../files") -const { Utils, Terminal } = require('./_utils') +import { join } from 'path' +import { Files } from "../files.js" +import { Utils, Terminal } from './_utils.js' let helpMessage = [ ` For example, if your project was called "${Terminal.cyan('my-cool-app')}"`, @@ -44,7 +44,7 @@ let run = async (options = {}) => { ].join('\n')) } - let isNonEmptyFolder = await Files.isNonEmptyFolder(path.join(process.cwd(), name)) + let isNonEmptyFolder = await Files.isNonEmptyFolder(join(process.cwd(), name)) if (isNonEmptyFolder) { return Promise.reject([ @@ -172,6 +172,4 @@ Please visit [the "Deployment" guide](https://elm.land/guide/deploying) to learn about deploying your app for free using Netlify or Vercel. `.trim() -module.exports = { - Init: { run, printHelpInfo } -} \ No newline at end of file +export const Init = { run, printHelpInfo } \ No newline at end of file diff --git a/projects/cli/src/commands/routes.js b/projects/cli/src/commands/routes.js index f67a2358..157aaaed 100644 --- a/projects/cli/src/commands/routes.js +++ b/projects/cli/src/commands/routes.js @@ -1,6 +1,6 @@ -const path = require('path') -const { Files } = require("../files") -const { Utils, Terminal } = require("./_utils") +import { join, sep } from 'path' +import { Files } from "../files.js" +import { Utils, Terminal } from "./_utils.js" let printHelpInfo = () => { return { @@ -28,7 +28,7 @@ let run = async ({ url } = {}) => { let pages = [] try { - pages = await Files.listElmFilepathsInFolder(path.join(process.cwd(), 'src', 'Pages')) + pages = await Files.listElmFilepathsInFolder(join(process.cwd(), 'src', 'Pages')) } catch (_) { return Promise.reject([ '', @@ -67,7 +67,7 @@ let run = async ({ url } = {}) => { let lines = pages .sort(sortRoutes) - .map(segments => ` ${Terminal.cyan(`src/Pages/${segments}.elm`)} ... ${Terminal.pink(toUrl(segments.split(path.sep)))}`) + .map(segments => ` ${Terminal.cyan(`src/Pages/${segments}.elm`)} ... ${Terminal.pink(toUrl(segments.split(sep)))}`) let lengthOfFilepath = (str) => str.split('...')[0].length @@ -110,8 +110,6 @@ const sortRoutes = (a, b) => { } -module.exports = { - Routes: { - run, printHelpInfo - } +export const Routes = { + run, printHelpInfo } \ No newline at end of file diff --git a/projects/cli/src/commands/server.js b/projects/cli/src/commands/server.js index 1c4d4d69..8fe8acfb 100644 --- a/projects/cli/src/commands/server.js +++ b/projects/cli/src/commands/server.js @@ -1,6 +1,6 @@ -const { Files } = require("../files") -const { Utils, Terminal } = require("./_utils") +import { Files } from "../files.js" +import { Utils, Terminal } from "./_utils.js" let printHelpInfo = () => { return { @@ -54,8 +54,6 @@ let run = async () => { } } -module.exports = { - Server: { - run, printHelpInfo - } +export const Server = { + run, printHelpInfo } \ No newline at end of file diff --git a/projects/cli/src/docs.js b/projects/cli/src/docs.js index 34a8729e..bd68dd62 100644 --- a/projects/cli/src/docs.js +++ b/projects/cli/src/docs.js @@ -1,10 +1,14 @@ -const fs = require('fs') -const path = require('path') +import { readFile } from 'fs' +import { join } from 'path' +import path from 'path' +import url from 'url' + +let __dirname = path.dirname(url.fileURLToPath(import.meta.url)) let read = async (filepath) => { let pieces = filepath.split('/') - let content = await new Promise((resolve, reject) => fs.readFile( - path.join(__dirname, '..', '..', 'docs', ...pieces), + let content = await new Promise((resolve, reject) => readFile( + join(__dirname, '..', '..', 'docs', ...pieces), { encoding: 'utf-8' }, (err, data) => { if (err) { reject(err) } else { resolve(data) } @@ -13,6 +17,4 @@ let read = async (filepath) => { return content.split('\r').join('') } -module.exports = { - Docs: { read } -} \ No newline at end of file +export const Docs = { read } \ No newline at end of file diff --git a/projects/cli/src/effects.js b/projects/cli/src/effects.js index b767dc3d..91ee5096 100644 --- a/projects/cli/src/effects.js +++ b/projects/cli/src/effects.js @@ -1,18 +1,19 @@ -const chokidar = require('chokidar') -const path = require('path') -const Vite = require('vite') -const ElmVitePlugin = require('./vite-plugins/elm/index.js') -const TypeScriptPlugin = require('./vite-plugins/typescript/index.js') -const DotPathFixPlugin = require('./vite-plugins/dot-path-fix/index.js') -const { Codegen } = require('./codegen') -const { Files } = require('./files') -const { Utils, Terminal } = require('./commands/_utils') -const { validate } = require('./validate/index.js') -const { default: ElmErrorJson } = require('./vite-plugins/elm/elm-error-json.js') - - -let srcPagesFolderFilepath = path.join(process.cwd(), 'src', 'Pages') -let srcLayoutsFolderFilepath = path.join(process.cwd(), 'src', 'Layouts') +import { watch } from 'chokidar' +import { join } from 'path' +import { createServer, loadEnv, build as _build } from 'vite' +import ElmVitePlugin from 'vite-plugin-elm-watch' +import * as ElmErrorJson from 'vite-plugin-elm-watch/src/elm-error-json.js' +import * as TypeScriptPlugin from './vite-plugins/typescript/index.js' +import { Codegen } from './codegen.js' +import { Files } from './files.js' +import { Utils, Terminal } from './commands/_utils.js' +import { validate } from './validate/index.js' +import path from 'path' +import url from 'url' + +let __dirname = path.dirname(url.fileURLToPath(import.meta.url)) +let srcPagesFolderFilepath = join(process.cwd(), 'src', 'Pages') +let srcLayoutsFolderFilepath = join(process.cwd(), 'src', 'Layouts') process.on('uncaughtException', function (err) { if (err.code === 'EPERM') { @@ -30,7 +31,7 @@ process.on('uncaughtException', function (err) { } else { throw err } -}); +}) const mode = () => @@ -51,36 +52,34 @@ let runServer = async (options) => { // Expose ENV variables explicitly allowed by the user handleEnvironmentVariables({ config }) - // Listen for changes to the "src" folder, so the browser - // automatically refreshes when an Elm file is changed - let srcFolder = `${path.join(process.cwd(), 'src')}/**/*.elm` - let srcFolderWatcher = chokidar.watch(srcFolder, { - ignorePermissionErrors: true, - ignoreInitial: true - }) - let mainElmPath = path.join(process.cwd(), '.elm-land', 'src', 'Main.elm') + // // Listen for changes to the "src" folder, so the browser + // // automatically refreshes when an Elm file is changed + // let srcFolder = `${join(process.cwd(), 'src')}/**/*.elm` + // let srcFolderWatcher = watch(srcFolder, { + // ignorePermissionErrors: true, + // ignoreInitial: true + // }) + // let mainElmPath = join(process.cwd(), '.elm-land', 'src', 'Main.elm') - srcFolderWatcher.on('all', () => { - Files.touch(mainElmPath) - }) + // srcFolderWatcher.on('all', () => { + // Files.touch(mainElmPath) + // }) // Listen for changes to static assets, so the browser // automatically shows the latest asset changes - let staticFolder = `${path.join(process.cwd(), 'static')}/**` - let staticFolderWatcher = chokidar.watch(staticFolder, { + let staticFolder = `${join(process.cwd(), 'static')}/**` + let staticFolderWatcher = watch(staticFolder, { ignorePermissionErrors: true, ignoreInitial: true }) - let indexHtmlPath = path.join(process.cwd(), '.elm-land', 'server', 'index.html') - staticFolderWatcher.on('all', () => { - Files.touch(indexHtmlPath) + Files.touch(mainJsPath) }) // Listen for config file changes, regenerating the index.html // and restart server in case there were any changes to the environment variables - let configFilepath = path.join(process.cwd(), 'elm-land.json') - let configFileWatcher = chokidar.watch(configFilepath, { + let configFilepath = join(process.cwd(), 'elm-land.json') + let configFileWatcher = watch(configFilepath, { ignorePermissionErrors: true, ignoreInitial: true }) @@ -107,8 +106,8 @@ let runServer = async (options) => { }) // Listen for `.env` file changes, and restart the dev server - let envFilepath = path.join(process.cwd(), '.env') - let envFileWatcher = chokidar.watch(envFilepath, { + let envFilepath = join(process.cwd(), '.env') + let envFileWatcher = watch(envFilepath, { ignorePermissionErrors: true, ignoreInitial: true }) @@ -119,9 +118,9 @@ let runServer = async (options) => { // Listen for changes to interop file, so the page is automatically // refreshed and can see JS changes - let interopFilepath = path.join(process.cwd(), 'src', 'interop.js') - let mainJsPath = path.join(process.cwd(), '.elm-land', 'server', 'main.js') - let interopFileWatcher = chokidar.watch(interopFilepath, { + let interopFilepath = join(process.cwd(), 'src', 'interop.js') + let mainJsPath = join(process.cwd(), '.elm-land', 'server', 'main.js') + let interopFileWatcher = watch(interopFilepath, { ignorePermissionErrors: true, ignoreInitial: true }) @@ -132,15 +131,15 @@ let runServer = async (options) => { // Listen for changes to src/Pages and src/Layouts folders, to prevent // generated code from getting out of sync - let srcPagesAndLayoutsAndCustomizedFileWatcher = chokidar.watch([ + let srcPagesAndLayoutsAndCustomizedFileWatcher = watch([ srcPagesFolderFilepath, srcLayoutsFolderFilepath, - path.join(process.cwd(), 'src', 'Auth.elm'), - path.join(process.cwd(), 'src', 'Shared.elm'), - path.join(process.cwd(), 'src', 'Shared', 'Model.elm'), - path.join(process.cwd(), 'src', 'Shared', 'Msg.elm'), - path.join(process.cwd(), 'src', 'Effect.elm'), - path.join(process.cwd(), 'src', 'View.elm') + join(process.cwd(), 'src', 'Auth.elm'), + join(process.cwd(), 'src', 'Shared.elm'), + join(process.cwd(), 'src', 'Shared', 'Model.elm'), + join(process.cwd(), 'src', 'Shared', 'Msg.elm'), + join(process.cwd(), 'src', 'Effect.elm'), + join(process.cwd(), 'src', 'View.elm') ], { ignorePermissionErrors: true, ignoreInitial: true @@ -153,8 +152,8 @@ let runServer = async (options) => { // if the customized versions are deleted let customizableFileFilepaths = Object.values(Utils.customizableFiles) - .flatMap(({ filepaths }) => filepaths.map(filepath => path.join(process.cwd(), 'src', ...filepath.target.split('/')))) - let customizedFilepaths = chokidar.watch(customizableFileFilepaths, { + .flatMap(({ filepaths }) => filepaths.map(filepath => join(process.cwd(), 'src', ...filepath.target.split('/')))) + let customizedFilepaths = watch(customizableFileFilepaths, { ignorePermissionErrors: true, ignoreInitial: true }) @@ -170,13 +169,60 @@ let runServer = async (options) => { try { proxy = config.app.proxy } catch (_) { } + /** + * This plugin allows me to keep the `index.html` file out of + * the root of the repository. + * + * It works by replacing the built-in 'viteIndexHtmlMiddleware' middleware + * at runtime with one that provides a "virtual" index.html file instead. + * + */ + const ElmLandIndexHtml = { + /** + * @returns {import('vite').Plugin} + */ + plugin() { + return { + name: 'elmLandIndexHtml', + configureServer(server_) { + let virtualIndexHtmlHandler = async function elmLandIndexHtmlMiddleware(req, res, next) { + let virtualIndexHtmlContents = toIndexHtmlFile(config, './.elm-land/server/main.js') + res.setHeader('Content-Type', 'text/html') + res.end(virtualIndexHtmlContents) + } + + setTimeout(() => { + for (let index in server_.middlewares.stack) { + let item = server_.middlewares.stack[index] + if (item.handle.name === 'viteIndexHtmlMiddleware') { + let viteIndexHtmlMiddleware = item.handle + item.handle = async function viteIndexHtmlMiddlewareMod(req, res, next) { + return new Promise(async (resolve, reject) => { + let myNext = () => virtualIndexHtmlHandler(req, res, next).then(_ => resolve()).catch(reject) + await viteIndexHtmlMiddleware(req, res, myNext) + }) + } + return + } + } + + // Only prints if a new version of Vite changed the name + // "viteIndexHtmlMiddleware" + console.error('‼️ FATAL ', 'viteIndexHtmlMiddleware was not found') + }, 0) + } + } + } + } + // Run the vite server on options.port - server = await Vite.createServer({ + server = await createServer({ configFile: false, - root: path.join(process.cwd(), '.elm-land', 'server'), - publicDir: path.join(process.cwd(), 'static'), + root: process.cwd(), + publicDir: join(process.cwd(), 'static'), envDir: process.cwd(), envPrefix: 'ELM_LAND_', + cacheDir: join(process.cwd(), '.elm-land', 'server', '.vite'), server: { host: options.host, port: options.port, @@ -184,21 +230,24 @@ let runServer = async (options) => { proxy }, plugins: [ - DotPathFixPlugin.plugin({ proxy }), - ElmVitePlugin.plugin({ - debug, - optimize: false + ElmVitePlugin({ + mode: debug ? 'debug' : 'standard', + isBodyPatchEnabled: false }), - TypeScriptPlugin.plugin() + ElmLandIndexHtml.plugin() ], - logLevel: 'silent' + logLevel: 'silent', + appType: 'spa' }) server.ws.on('error', (e) => console.error(e)) - server.ws.on('elm:client-ready', () => { + server.ws.on('elm:client-ready', (client) => { + id = client.id if (lastErrorSent) { + let error = ElmErrorJson.toColoredHtmlOutput(lastErrorSent) server.ws.send('elm:error', { - error: ElmErrorJson.toColoredHtmlOutput(lastErrorSent) + id, + error }) } }) @@ -216,6 +265,7 @@ let runServer = async (options) => { } let lastErrorSent = undefined +let id = null let generateElmFiles = async (config, server = undefined) => { try { @@ -256,41 +306,44 @@ let generateElmFiles = async (config, server = undefined) => { view }) - if (errors.length === 0) { - if (server) { - lastErrorSent = null - server.ws.send('elm:success', { msg: 'Success!' }) - } + // Always generate Elm Land files + let layoutsData = layouts.map(({ filepath, contents }) => { + const typeVariablePattern = 'type alias Props contentMsg' + const isUsingTypeVariable = contents.includes(typeVariablePattern) - let layoutsData = layouts.map(({ filepath, contents }) => { - const typeVariablePattern = 'type alias Props contentMsg'; - const isUsingTypeVariable = contents.includes(typeVariablePattern); + return { + segments: filepath, + isUsingTypeVariable + } + }) - return { - segments: filepath, - isUsingTypeVariable - } - }) + let newFiles = await Codegen.generateElmLandFiles({ + pages, + layouts: layoutsData, + router + }) - let newFiles = await Codegen.generateElmLandFiles({ - pages, - layouts: layoutsData, - router - }) + await Files.create( + newFiles.map(generatedFile => ({ + kind: 'file', + name: `.elm-land/src/${generatedFile.filepath}`, + content: generatedFile.contents + })) + ) - await Files.create( - newFiles.map(generatedFile => ({ - kind: 'file', - name: `.elm-land/src/${generatedFile.filepath}`, - content: generatedFile.contents - })) - ) + if (errors.length === 0) { + if (server) { + lastErrorSent = undefined + server.ws.send('elm:success', { id }) + } } else if (server) { lastErrorSent = errors[0] server.ws.send('elm:error', { + id, error: ElmErrorJson.toColoredHtmlOutput(errors[0]) }) } else { + // console.log({ errors }) return Promise.reject([ '', Utils.intro.error('failed to build project'), @@ -308,7 +361,7 @@ let generateElmFiles = async (config, server = undefined) => { let handleEnvironmentVariables = ({ config }) => { try { if (config && config.app && config.app.env && Array.isArray(config.app.env)) { - const env = Vite.loadEnv(mode(), process.cwd(), '') + const env = loadEnv(mode(), process.cwd(), '') let allowed = config.app.env.reduce((obj, key) => { obj[key] = env[key] return obj @@ -347,8 +400,8 @@ const attempt = (fn) => { const customize = async (filepaths) => { await Promise.all( filepaths.map(async filepath => { - let source = path.join(__dirname, 'templates', '_elm-land', 'customizable', ...filepath.src.split('/')) - let destination = path.join(process.cwd(), 'src', ...filepath.target.split('/')) + let source = join(__dirname, 'templates', '_elm-land', 'customizable', ...filepath.src.split('/')) + let destination = join(process.cwd(), 'src', ...filepath.target.split('/')) let alreadyExists = await Files.exists(destination) @@ -361,7 +414,7 @@ const customize = async (filepaths) => { } try { - await Files.remove(path.join(process.cwd(), '.elm-land', 'src', ...filepath.target.split('/'))) + await Files.remove(join(process.cwd(), '.elm-land', 'src', ...filepath.target.split('/'))) } catch (_) { // If the file isn't there, no worries } @@ -379,9 +432,9 @@ const syncCustomizableFiles = async () => { .filter(filepath => filepath.src === filepath.target) await Promise.all(defaultFilepaths.map(async filepath => { - let fileInUsersSrcFolder = path.join(process.cwd(), 'src', ...filepath.target.split('/')) - let fileInTemplatesFolder = path.join(__dirname, 'templates', '_elm-land', 'customizable', ...filepath.src.split('/')) - let fileInElmLandSrcFolder = path.join(process.cwd(), '.elm-land', 'src', ...filepath.target.split('/')) + let fileInUsersSrcFolder = join(process.cwd(), 'src', ...filepath.target.split('/')) + let fileInTemplatesFolder = join(__dirname, 'templates', '_elm-land', 'customizable', ...filepath.src.split('/')) + let fileInElmLandSrcFolder = join(process.cwd(), '.elm-land', 'src', ...filepath.target.split('/')) let usersSrcFileExists = await Files.exists(fileInUsersSrcFolder) @@ -404,12 +457,12 @@ const handleElmLandFiles = async () => { await syncCustomizableFiles() await Files.copyPasteFolder({ - source: path.join(__dirname, 'templates', '_elm-land', 'server'), - destination: path.join(process.cwd(), '.elm-land'), + source: join(__dirname, 'templates', '_elm-land', 'server'), + destination: join(process.cwd(), '.elm-land'), }) await Files.copyPasteFolder({ - source: path.join(__dirname, 'templates', '_elm-land', 'src'), - destination: path.join(process.cwd(), '.elm-land'), + source: join(__dirname, 'templates', '_elm-land', 'src'), + destination: join(process.cwd(), '.elm-land'), }) } @@ -436,19 +489,17 @@ const build = async (config) => { // Build app in dist folder try { - await Vite.build({ + await _build({ configFile: false, - root: path.join(process.cwd(), '.elm-land', 'server'), - publicDir: path.join(process.cwd(), 'static'), - build: { - outDir: '../../dist' - }, + root: join(process.cwd(), '.elm-land', 'server'), + publicDir: join(process.cwd(), 'static'), + build: { outDir: '../../dist' }, envDir: process.cwd(), envPrefix: 'ELM_LAND_', plugins: [ - ElmVitePlugin.plugin({ - debug: false, - optimize: true + ElmVitePlugin({ + mode: 'minify', + isBodyPatchEnabled: false }) ], logLevel: 'silent' @@ -509,8 +560,7 @@ const handleViteBuildErrors = (err) => { } // Generating index.html from elm-land.json file -const generateHtml = async (config) => { - +const toIndexHtmlFile = (config, relativePathToMainJs = './main.js') => { const escapeHtml = (unsafe) => { return unsafe .split('&',).join('&') @@ -573,7 +623,7 @@ const generateHtml = async (config) => { ? [toHtmlTag('title', {}, config.app.html.title)] : [] let metaTags = toSelfClosingHtmlTags('meta', [ - { name: 'elm-land', content: '0.19.5' } + { name: 'elm-land', content: '0.20.0' } ].concat(attempt(_ => config.app.html.meta))) let linkTags = toSelfClosingHtmlTags('link', attempt(_ => config.app.html.link)) let scriptTags = toHtmlTags('script', attempt(_ => config.app.html.script)) @@ -584,20 +634,22 @@ const generateHtml = async (config) => { : '' let htmlContent = ` - - ${headTags} - -
- - + +${headTags} + + + ` + return htmlContent +} +const generateHtml = async (config) => { try { await Files.create([ { kind: 'file', name: '.elm-land/server/index.html', - content: htmlContent + content: toIndexHtmlFile(config) } ]) return { problem: null } @@ -610,7 +662,7 @@ const generateHtml = async (config) => { let run = async (effects) => { // 1. Perform all effects, one at a time let results = [] - let port = undefined; + let port = undefined for (let effect of effects) { switch (effect.kind) { @@ -648,6 +700,4 @@ let run = async (effects) => { return { problem: null, port } } -module.exports = { - Effects: { run } -} +export const Effects = { run } diff --git a/projects/cli/src/files.js b/projects/cli/src/files.js index dca25270..4438da32 100644 --- a/projects/cli/src/files.js +++ b/projects/cli/src/files.js @@ -1,5 +1,9 @@ -const fs = require('fs') -const path = require('path') +import { rmSync, access, mkdir, existsSync, lstatSync, writeFileSync, readFileSync, mkdirSync, readdirSync, writeFile, utimesSync, statSync } from 'fs' +import { sep, join, basename } from 'path' +import path from 'path' +import url from 'url' + +let __dirname = path.dirname(url.fileURLToPath(import.meta.url)) let create = async (filesAndFolders) => { await Promise.all(filesAndFolders.map(item => { @@ -15,14 +19,14 @@ let create = async (filesAndFolders) => { } let remove = async (filepath) => { - fs.rmSync(filepath) + rmSync(filepath) } // Determines if a file or folder exists let exists = async (filepath) => { try { return new Promise((resolve, reject) => { - fs.access(filepath, (err) => { + access(filepath, (err) => { if (err) { resolve(false) } else { resolve(true) } }) }) @@ -35,7 +39,7 @@ let exists = async (filepath) => { let copyPasteFolder = async ({ source, destination }) => { // Make sure destination folder exists first! await new Promise((resolve, reject) => { - fs.mkdir(destination, { recursive: true }, (err, path) => { + mkdir(destination, { recursive: true }, (err, path) => { if (err) { reject(err) } else { @@ -49,9 +53,9 @@ let copyPasteFolder = async ({ source, destination }) => { let copyPasteFile = async ({ source, destination }) => { // Ensure folder exists before pasting file - let destinationFolder = destination.split(path.sep).slice(0, -1).join(path.sep) + let destinationFolder = destination.split(sep).slice(0, -1).join(sep) await new Promise((resolve, reject) => { - fs.mkdir(destinationFolder, { recursive: true }, (err, path) => { + mkdir(destinationFolder, { recursive: true }, (err, path) => { if (err) { reject(err) } else { @@ -65,38 +69,38 @@ let copyPasteFile = async ({ source, destination }) => { function copyFileSync(source, target) { - var targetFile = target; + var targetFile = target // If target is a directory, a new file with the same name will be created - if (fs.existsSync(target)) { - if (fs.lstatSync(target).isDirectory()) { - targetFile = path.join(target, path.basename(source)); + if (existsSync(target)) { + if (lstatSync(target).isDirectory()) { + targetFile = join(target, basename(source)) } } - fs.writeFileSync(targetFile, fs.readFileSync(source)); + writeFileSync(targetFile, readFileSync(source)) } function copyFolderRecursiveSync(source, target) { - var files = []; + var files = [] // Check if folder needs to be created or integrated - var targetFolder = path.join(target, path.basename(source)); - if (!fs.existsSync(targetFolder)) { - fs.mkdirSync(targetFolder); + var targetFolder = join(target, basename(source)) + if (!existsSync(targetFolder)) { + mkdirSync(targetFolder) } // Copy - if (fs.lstatSync(source).isDirectory()) { - files = fs.readdirSync(source); + if (lstatSync(source).isDirectory()) { + files = readdirSync(source) files.forEach(function (file) { - var curSource = path.join(source, file); - if (fs.lstatSync(curSource).isDirectory()) { - copyFolderRecursiveSync(curSource, targetFolder); + var curSource = join(source, file) + if (lstatSync(curSource).isDirectory()) { + copyFolderRecursiveSync(curSource, targetFolder) } else { - copyFileSync(curSource, targetFolder); + copyFileSync(curSource, targetFolder) } - }); + }) } } @@ -107,8 +111,8 @@ let createFile = async ({ name, content }) => { await createFolder({ name: containingFolder }) await new Promise((resolve, reject) => { - fs.writeFile( - path.join(process.cwd(), ...pieces), + writeFile( + join(process.cwd(), ...pieces), content, { encoding: 'utf-8' }, (err) => { if (err) { @@ -123,8 +127,8 @@ let createFile = async ({ name, content }) => { let createFolder = async ({ name }) => { return new Promise((resolve, reject) => { - fs.mkdir( - path.join(process.cwd(), ...name.split('/')), + mkdir( + join(process.cwd(), ...name.split('/')), { recursive: true }, (err, path) => { if (err) { @@ -138,8 +142,8 @@ let createFolder = async ({ name }) => { let readFromCliFolder = async (filepath) => { let pieces = filepath.split('/') - let content = fs.readFileSync( - path.join(__dirname, '..', ...pieces), + let content = readFileSync( + join(__dirname, '..', ...pieces), { encoding: 'utf-8' } ) return content.split('\r').join('') @@ -147,8 +151,8 @@ let readFromCliFolder = async (filepath) => { let readFromUserFolder = async (filepath) => { let pieces = filepath.split('/') - let content = fs.readFileSync( - path.join(process.cwd(), ...pieces), + let content = readFileSync( + join(process.cwd(), ...pieces), { encoding: 'utf-8' } ) return content.split('\r').join('') @@ -157,12 +161,12 @@ let readFromUserFolder = async (filepath) => { // Pokes a file to trigger any related file-watchers let touch = (filepath) => { let now = new Date() - fs.utimesSync(filepath, now, now) + utimesSync(filepath, now, now) } // Read all the files in the current folder, recursively let listElmFilepathsInFolder = (filepath) => { - let folderExists = fs.existsSync(filepath) + let folderExists = existsSync(filepath) if (folderExists) { let fullFilepaths = walk(filepath) @@ -177,25 +181,25 @@ let listElmFilepathsInFolder = (filepath) => { } var walk = function (dir) { - var results = []; - var list = fs.readdirSync(dir); + var results = [] + var list = readdirSync(dir) list.forEach(function (file) { - file = dir + '/' + file; - var stat = fs.statSync(file); + file = dir + '/' + file + var stat = statSync(file) if (stat && stat.isDirectory()) { /* Recurse into a subdirectory */ - results = results.concat(walk(file)); + results = results.concat(walk(file)) } else { /* Is a file */ - results.push(file); + results.push(file) } - }); - return results; + }) + return results } let isNonEmptyFolder = async (filepath) => { try { - return fs.readdirSync(filepath).length > 0 + return readdirSync(filepath).length > 0 } catch (_) { // Crashes if folder does not exist, in which // case it is NOT an non-empty folder @@ -203,17 +207,15 @@ let isNonEmptyFolder = async (filepath) => { } } -module.exports = { - Files: { - isNonEmptyFolder, - readFromCliFolder, - readFromUserFolder, - create, - remove, - exists, - copyPasteFolder, - copyPasteFile, - touch, - listElmFilepathsInFolder - } +export const Files = { + isNonEmptyFolder, + readFromCliFolder, + readFromUserFolder, + create, + remove, + exists, + copyPasteFolder, + copyPasteFile, + touch, + listElmFilepathsInFolder } \ No newline at end of file diff --git a/projects/cli/src/index.js b/projects/cli/src/index.js index 5edffce3..1aa0a863 100755 --- a/projects/cli/src/index.js +++ b/projects/cli/src/index.js @@ -1,7 +1,7 @@ #! /usr/bin/env node -const { Cli } = require('./cli') -const { Effects } = require('./effects') -const { Files } = require('./files') +import { Cli } from './cli.js' +import { Effects } from './effects.js' +import { Files } from './files.js' let main = async () => { try { diff --git a/projects/cli/src/templates/_elm-land/customizable/Auth.elm b/projects/cli/src/templates/_elm-land/customizable/Auth.elm index 5c3d8fad..d9883ca9 100644 --- a/projects/cli/src/templates/_elm-land/customizable/Auth.elm +++ b/projects/cli/src/templates/_elm-land/customizable/Auth.elm @@ -1,4 +1,4 @@ -module Auth exposing (User, onPageLoad, viewLoadingPage) +module Auth exposing (User, onPageLoad, viewCustomPage) import Auth.Action import Dict @@ -23,8 +23,8 @@ onPageLoad shared route = } -{-| Renders whenever `Auth.Action.showLoadingPage` is returned from `onPageLoad`. +{-| Renders whenever `Auth.Action.loadCustomPage` is returned from `onPageLoad`. -} -viewLoadingPage : Shared.Model -> Route () -> View Never -viewLoadingPage shared route = +viewCustomPage : Shared.Model -> Route () -> View Never +viewCustomPage shared route = View.fromString "Loading..." diff --git a/projects/cli/src/templates/_elm-land/server/main.js b/projects/cli/src/templates/_elm-land/server/main.js index 0808b44e..ea8649c9 100644 --- a/projects/cli/src/templates/_elm-land/server/main.js +++ b/projects/cli/src/templates/_elm-land/server/main.js @@ -1,83 +1,4 @@ -import { Elm } from '../src/Main.elm' - -// client side -if (import.meta.hot) { - class ElmErrorOverlay extends HTMLElement { - constructor() { - super() - this.attachShadow({ mode: 'open' }) - } - - onContentChanged(html) { - this.shadowRoot.querySelector('.elm-error').innerHTML = html - } - - connectedCallback() { - this.shadowRoot.innerHTML = ` - -
-
- ` - } - } - - import.meta.hot.on('elm:error', (data) => { - - if (!customElements.get('elm-error-overlay')) { - customElements.define('elm-error-overlay', ElmErrorOverlay) - } - - let existingOverlay = document.querySelector('elm-error-overlay') - - if (existingOverlay) { - existingOverlay.onContentChanged(data.error) - } else { - document.body.innerHTML += '' - document.querySelector('elm-error-overlay').onContentChanged(data.error) - } - - }) - - import.meta.hot.on('elm:success', () => { - let existingOverlay = document.querySelector('elm-error-overlay') - if (existingOverlay) { - existingOverlay.remove() - } - }) - -} +import Main from '../src/Main.elm' let startApp = async ({ Interop }) => { // Grab environment variables, but remove the "ELM_LAND_" prefix @@ -94,8 +15,8 @@ let startApp = async ({ Interop }) => { flags = await Interop.flags({ env }) } - if (Elm && Elm.Main && Elm.Main.init) { - let app = Elm.Main.init({ + if (Main && Main.init) { + let app = Main.init({ node: document.getElementById('app'), flags }) @@ -105,10 +26,6 @@ let startApp = async ({ Interop }) => { } } - if (import.meta.env.DEV) { - import.meta.hot.send('elm:client-ready') - } - } diff --git a/projects/cli/src/templates/_elm-land/src/Auth/Action.elm b/projects/cli/src/templates/_elm-land/src/Auth/Action.elm index 71f005b3..597ed784 100644 --- a/projects/cli/src/templates/_elm-land/src/Auth/Action.elm +++ b/projects/cli/src/templates/_elm-land/src/Auth/Action.elm @@ -1,6 +1,6 @@ module Auth.Action exposing ( Action(..) - , loadPageWithUser, showLoadingPage + , loadPageWithUser, loadCustomPage , replaceRoute, pushRoute, loadExternalUrl , view, subscriptions, command ) @@ -8,7 +8,7 @@ module Auth.Action exposing {-| @docs Action -@docs loadPageWithUser, showLoadingPage +@docs loadPageWithUser, loadCustomPage @docs replaceRoute, pushRoute, loadExternalUrl @docs view, subscriptions, command @@ -23,9 +23,12 @@ import Url exposing (Url) import View exposing (View) +{-| Describes the action to take for authenticated pages, based +on the current `Route` and `Shared.Model` +-} type Action user = LoadPageWithUser user - | ShowLoadingPage (View Never) + | LoadCustomPage | ReplaceRoute { path : Route.Path.Path , query : Dict String String @@ -39,16 +42,27 @@ type Action user | LoadExternalUrl String +{-| Successfully pass the user along to the authenticated page. +-} loadPageWithUser : user -> Action user loadPageWithUser = LoadPageWithUser -showLoadingPage : View Never -> Action user -showLoadingPage = - ShowLoadingPage +{-| Rather than navigating to a different route, keep the URL, but render +what was defined in `Auth.loadCustomPage`. + +**Note:** `Auth.loadCustomPage` has access to the `Shared.Model`, so you +can render different pages in different authentication scenarios. + +-} +loadCustomPage : Action user +loadCustomPage = + LoadCustomPage +{-| Replace the URL with the provided route. +-} replaceRoute : { path : Route.Path.Path , query : Dict String String @@ -59,6 +73,8 @@ replaceRoute = ReplaceRoute +{-| Push a new URL with the provided route. +-} pushRoute : { path : Route.Path.Path , query : Dict String String @@ -69,6 +85,8 @@ pushRoute = PushRoute +{-| Navigate to a URL for an external website. +-} loadExternalUrl : String -> Action user loadExternalUrl = LoadExternalUrl @@ -78,14 +96,14 @@ loadExternalUrl = -- USED INTERNALLY BY ELM LAND -view : (user -> View msg) -> Action user -> View msg -view toView authAction = +view : View msg -> (user -> View msg) -> Action user -> View msg +view viewCustomPage viewPageWithUser authAction = case authAction of LoadPageWithUser user -> - toView user + viewPageWithUser user - ShowLoadingPage loadingView -> - View.map never loadingView + LoadCustomPage -> + viewCustomPage ReplaceRoute _ -> View.none @@ -103,7 +121,7 @@ subscriptions toSub authAction = LoadPageWithUser user -> toSub user - ShowLoadingPage _ -> + LoadCustomPage -> Sub.none ReplaceRoute _ -> @@ -122,7 +140,7 @@ command toCmd authAction = LoadPageWithUser user -> toCmd user - ShowLoadingPage _ -> + LoadCustomPage -> Cmd.none ReplaceRoute _ -> diff --git a/projects/cli/src/validate/index.js b/projects/cli/src/validate/index.js index 04ff5e22..7eae67e9 100644 --- a/projects/cli/src/validate/index.js +++ b/projects/cli/src/validate/index.js @@ -1,13 +1,14 @@ -const path = require('path') +import { join } from 'path' -let validate = async (flags) => { - let { Elm } = require('../../dist/validate-worker.js') +export const validate = async (flags) => { + let result = await import('../../dist/validate-worker.cjs') + let Elm = result.default.Elm let elmLandErrors = await new Promise((resolve) => { let app = Elm.Worker.init({ flags }) app.ports.onComplete.subscribe(errors => { errors.forEach(error => { - error.path = path.join(process.cwd(), error.path) + error.path = join(process.cwd(), error.path) }) resolve(errors) }) @@ -15,7 +16,3 @@ let validate = async (flags) => { return elmLandErrors } - -module.exports = { - validate -} \ No newline at end of file diff --git a/projects/cli/src/validate/src/Worker.elm b/projects/cli/src/validate/src/Worker.elm index 5bfd6a9e..21dd8028 100644 --- a/projects/cli/src/validate/src/Worker.elm +++ b/projects/cli/src/validate/src/Worker.elm @@ -120,7 +120,7 @@ init flags = { types = [ "User" ] , functions = [ ( "onPageLoad", "Shared.Model -> Route () -> Auth.Action.Action User" ) - , ( "viewLoadingPage", "Shared.Model -> Route () -> View Never" ) + , ( "viewCustomPage", "Shared.Model -> Route () -> View Never" ) ] } , toCustomizableErrors customizedFiles.shared diff --git a/projects/cli/src/vite-plugins/dot-path-fix/index.js b/projects/cli/src/vite-plugins/dot-path-fix/index.js deleted file mode 100644 index be3cd1ab..00000000 --- a/projects/cli/src/vite-plugins/dot-path-fix/index.js +++ /dev/null @@ -1,37 +0,0 @@ -const fs = require('fs') -const path = require('path') - -const plugin = (props = {}) => { - return { - name: 'dot-path-fix-plugin', - configureServer: (server) => { - server.middlewares.use((req, _, next) => { - const reqPath = req.url.split('?', 2)[0] - - // Ignore this logic if the proxy should handle it instead - if (props.proxy) { - for (let key in props.proxy) { - let match = new RegExp(key).exec(reqPath) - if (match) { - return next() - } - } - } - - const publicReqPath = path.join(server.config.publicDir, decodeURI(reqPath)); - if (reqPath == '/main.js') { - next() - } else { - if (!req.url.startsWith('/@') && !fs.existsSync(`.${reqPath}`) && !fs.existsSync(`${publicReqPath}`)) { - req.url = '/'; - } - next(); - } - }); - } - } -}; - -module.exports = { - plugin -} diff --git a/projects/cli/src/vite-plugins/elm/README.md b/projects/cli/src/vite-plugins/elm/README.md deleted file mode 100644 index f17d0cf8..00000000 --- a/projects/cli/src/vite-plugins/elm/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# @elm-land/vite-plugin-elm -> A plugin that shows full-color Elm compiler errors in the HMR overlay - diff --git a/projects/cli/src/vite-plugins/elm/elm-error-json.js b/projects/cli/src/vite-plugins/elm/elm-error-json.js deleted file mode 100644 index 60b02ad4..00000000 --- a/projects/cli/src/vite-plugins/elm/elm-error-json.js +++ /dev/null @@ -1,236 +0,0 @@ -"use strict"; -var __assign = (this && this.__assign) || function () { - __assign = Object.assign || function (t) { - for (var s, i = 1, n = arguments.length; i < n; i++) { - s = arguments[i]; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) - t[p] = s[p]; - } - return t; - }; - return __assign.apply(this, arguments); -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function () { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; - return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function () { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (_) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.toColoredTerminalOutput = void 0; -var nodeElmCompiler = require('node-elm-compiler'); -var compile = function (path) { - return __awaiter(void 0, void 0, void 0, function () { - return __generator(this, function (_a) { - return [2 /*return*/, toRawJsonString(path).then(parse)]; - }); - }); -}; -var toRawJsonString = function (path) { - return __awaiter(void 0, void 0, void 0, function () { - return __generator(this, function (_a) { - return [2 /*return*/, nodeElmCompiler - .compileToString([path], { report: 'json' }) - .catch(function (err) { return err.message.slice('Compilation failed\n'.length); })]; - }); - }); -}; -var parse = function (rawErrorString) { - // The error returned from node-elm-compiler's error.message - // contains this string before the JSON blob: - var nodeElmCompilerPreamble = "Compilation failed\n"; - var normalizedJsonString = (rawErrorString.indexOf(nodeElmCompilerPreamble) === 0) - ? rawErrorString.slice(nodeElmCompilerPreamble.length) - : rawErrorString; - try { - // Doing this `as` cast here is dangerous, because - // the caller can pass arbitrary JSON: - var json = JSON.parse(normalizedJsonString); - // To potentially prevent this cast from leading to - // unexpected errors, we validate it at least has - // the expected "type" values - if (json.type === 'compile-errors' || json.type === 'error') { - return json; - } - else { - console.error("JSON is valid, but result is not an Elm error", rawErrorString); - return undefined; - } - } - catch (e) { - console.error("Failed to decode an Elm error", rawErrorString); - return undefined; - } -}; -var toColoredHtmlOutput = function (elmError, colorMap = { - GREEN: 'mediumseagreen', - RED: 'indianred', - BLUE: 'dodgerblue', -}) { - var gap = "
"; - // These can be passed in - var colors = __assign({ RED: 'red', MAGENTA: 'magenta', YELLOW: 'yellow', GREEN: 'green', CYAN: 'cyan', BLUE: 'blue', BLACK: 'black', WHITE: 'white' }, colorMap); - var render = function (message) { - var messages = normalizeErrorMessages(message); - return messages.map(function (msg) { - var text = msg.string.split('\n'); - var lines = text.map(function (str) { - var style = {}; - if (msg.bold) { - style['font-weight'] = 'bold'; - } - if (msg.underline) { - style['text-decoration'] = 'underline'; - } - if (msg.color) { - style['color'] = colors[msg.color.toUpperCase()]; - } - var styleValue = Object.keys(style).map(function (k) { return "".concat(k, ": ").concat(style[k]); }).join('; '); - return "").concat(escapeHtml(str), ""); - }); - return lines.join(gap); - }).join(''); - }; - var attrs = "style=\"white-space: pre;\""; - switch (elmError.type) { - case 'compile-errors': - var lines = elmError.errors.map(function (error) { - return error.problems.map(function (problem) { - return [ - "".concat(escapeHtml(header(error, problem)), ""), - render(problem.message) - ].join(gap.repeat(2)); - }).join(gap.repeat(2)); - }); - return "
").concat(lines.join(gap.repeat(3)), "
"); - case 'error': - return [ - "".concat(escapeHtml(header(elmError, elmError)), ""), - "
").concat(render(elmError.message), "
") - ].join(gap.repeat(2)) - } -}; -// INTERNALS -/** - * Converts strings to styled messages, so we can easily - * apply formatting using an Array.map in view code - */ -var normalizeErrorMessages = function (messages) { - return messages.map(function (msg) { - return typeof msg === 'string' - ? { bold: false, underline: false, color: 'WHITE', string: msg } - : msg; - }); -}; -var header = function (error, problem, cwd_) { - var MAX_WIDTH = 80; - var SPACER = '-'; - var SPACING_COUNT = 2; - var PREFIX = '-- '; - var left = problem.title; - var cwd = cwd_ || process.cwd(); - - var absolutePath = error.path; - var relativePath = '' - if (absolutePath) { - relativePath = absolutePath.slice(cwd.length + 1); - } - var dashCount = MAX_WIDTH - left.length - PREFIX.length - SPACING_COUNT - relativePath.length; - return "".concat(PREFIX).concat(left, " ").concat(SPACER.repeat(dashCount < 0 ? 0 : dashCount), " ").concat(relativePath); -}; -var escapeHtml = function (str) { - return str - .split('<').join('<') - .split('>').join('>'); -}; -var toColoredTerminalOutput = function (elmError) { - // TERMINAL ASCII CODES - var code = function (num) { return "\u001b[" + num + "m"; }; - var reset = code(0); - var bold = code(1); - var underline = code(4); - var colors = { - RED: 31, - MAGENTA: 35, - YELLOW: 33, - GREEN: 32, - CYAN: 36, - BLUE: 34, - BLACK: 30, - WHITE: 37 - }; - var render = function (message) { - var messages = normalizeErrorMessages(message); - return messages.map(function (msg) { - var str = ''; - if (msg.bold) { - str += bold; - } - if (msg.underline) { - str += underline; - } - if (msg.color) { - str += code(colors[msg.color.toUpperCase()]); - } - str += msg.string; - str += reset; - return str; - }).join(''); - }; - switch (elmError.type) { - case 'compile-errors': - var output = elmError.errors.reduce(function (output, error) { - var problems = error.problems.map(function (problem) { - return [ - (code(colors.CYAN) + header(error, problem) + reset), - render(problem.message) - ].join('\n\n\n'); - }); - return output.concat(problems); - }, []); - return output.join('\n\n'); - case 'error': - return [ - (code(colors.CYAN) + header(elmError, elmError) + reset), - render(elmError.message) - ].join('\n\n') - } -}; -exports.toColoredTerminalOutput = toColoredTerminalOutput; -exports.default = { - compile: compile, - parse: parse, - toRawJsonString: toRawJsonString, - toColoredTerminalOutput: exports.toColoredTerminalOutput, - toColoredHtmlOutput: toColoredHtmlOutput -}; diff --git a/projects/cli/src/vite-plugins/elm/index.js b/projects/cli/src/vite-plugins/elm/index.js deleted file mode 100644 index 68eb6b3f..00000000 --- a/projects/cli/src/vite-plugins/elm/index.js +++ /dev/null @@ -1,218 +0,0 @@ -const compiler = require('node-elm-compiler') -const { relative } = require('path') -const { acquireLock } = require('./mutex') -const { default: ElmErrorJson } = require('./elm-error-json.js') -const terser = require('terser') -const path = require('path') -const fs = require('fs') - -const trimDebugMessage = (code) => code.replace(/(console\.warn\('Compiled in DEBUG mode)/, '// $1') -const viteProjectPath = (dependency) => `/${relative(process.cwd(), dependency)}` - -// Here's where we'll expect to find the Elm binary installed -const elmPaths = { - // When locally installed with `npm install -D elm-land` - // ✅ Tested with npm install -D, yarn, pnpm i - local: path.join(__dirname, '..', '..', '..', '..', 'elm', 'bin', 'elm'), - // When globally installed with `npm install -g elm-land` - // ✅ Tested with npm install -g, yarn, pnpm - global: path.join(__dirname, '..', '..', '..', 'node_modules', '.bin', 'elm'), -} - -const pathToElm = - fs.existsSync(elmPaths.global) - ? elmPaths.global - : elmPaths.local - -const parseImportId = (id) => { - const parsedId = new URL(id, 'file://') - const pathname = parsedId.pathname - const valid = pathname.endsWith('.elm') - const withParams = parsedId.searchParams.getAll('with') - - return { - valid, - pathname, - withParams, - } -} - -const plugin = (opts) => { - const compilableFiles = new Map() - const debug = opts ? opts.debug : undefined - const optimize = opts ? opts.optimize : undefined - - let lastErrorSent = undefined - let server = undefined - - return { - name: 'vite-plugin-elm', - enforce: 'pre', - handleHotUpdate({ file, server, modules }) { - const { valid } = parseImportId(file) - if (!valid) return - - const modulesToCompile = [] - compilableFiles.forEach((dependencies, compilableFile) => { - if (dependencies.has(file)) { - const module = server.moduleGraph.getModuleById(compilableFile) - if (module) modulesToCompile.push(module) - } - }) - - if (modulesToCompile.length > 0) { - server.ws.send({ - type: 'custom', - event: 'hot-update-dependents', - data: modulesToCompile.map(({ url }) => url), - }) - return modulesToCompile - } else { - return modules - } - }, - configureServer(server_) { - server = server_ - - server.ws.on('elm:client-ready', () => { - if (lastErrorSent) { - server.ws.send('elm:error', { - error: ElmErrorJson.toColoredHtmlOutput(lastErrorSent) - }) - } - }) - }, - async load(id) { - const { valid, pathname, withParams } = parseImportId(id) - if (!valid) return - - const accompanies = await (() => { - if (withParams.length > 0) { - const importTree = this.getModuleIds() - let importer = '' - for (const moduleId of importTree) { - if (moduleId === id) break - importer = moduleId - } - const resolveAcoompany = async (accompany) => { - let thing = await this.resolve(accompany, importer) - return thing && thing.id ? thing.id : '' - } - return Promise.all(withParams.map(resolveAcoompany)) - } else { - return Promise.resolve([]) - } - })() - - const targets = [pathname, ...accompanies].filter((target) => target !== '') - - compilableFiles.delete(id) - const dependencies = ( - await Promise.all(targets.map((target) => compiler.findAllDependencies(target))) - ).flat() - compilableFiles.set(id, new Set([...accompanies, ...dependencies])) - - const releaseLock = await acquireLock() - const isBuild = process.env.NODE_ENV === 'production' - try { - const compiled = await compiler.compileToString(targets, { - pathToElm, - output: '.js', - optimize: typeof optimize === 'boolean' ? optimize : !debug && isBuild, - verbose: false, - debug: typeof debug === 'boolean' ? debug : !isBuild, - report: 'json' - }) - - // Taken from https://www.npmjs.com/package/elm-esm/v/1.1.4 - // - // It is just a single function, so having an NPM dependency feels silly, but - // I want to still give source credit to https://github.com/ChristophP/elm-esm - const toESModule = (js) => { - const elmExports = js.match( - /^\s*_Platform_export\(([^]*)\);\n?}\(this\)\);/m - )[1] - return js - .replace(/\(function\s*\(scope\)\s*\{$/m, "// -- $&") - .replace(/['"]use strict['"];$/m, "// -- $&") - .replace(/function _Platform_export([^]*?)\}\n/g, "/*\n$&\n*/") - .replace(/function _Platform_mergeExports([^]*?)\}\n\s*}/g, "/*\n$&\n*/") - .replace(/^\s*_Platform_export\(([^]*)\);\n?}\(this\)\);/m, "/*\n$&\n*/") - .concat(`\nexport const Elm = ${elmExports};\n`) - } - - const esm = toESModule(compiled) - - // Apparently `addWatchFile` may not exist: https://github.com/hmsk/vite-plugin-elm/pull/36 - if (this.addWatchFile) { - dependencies.forEach(this.addWatchFile.bind(this)) - } - - // SUSPICIOUS, this line might make the error overlay clear unexpectedly - lastErrorSent = null - if (server) { - server.ws.send('elm:success', { msg: 'Success!' }) - } - - let minify = async (unminifiedJs) => { - // --compress 'pure_funcs="F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9",pure_getters,keep_fargs=false,unsafe_comps,unsafe' }) - const { code: step1 } = await terser.minify(unminifiedJs, { compress: { pure_funcs: 'F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9'.split(','), pure_getters: true, keep_fargs: false, unsafe_comps: true, unsafe: true } }) - // --mangle - const { code: step2 } = await terser.minify(step1, { mangle: true }) - return step2 - } - - if (isBuild) { - let code = await minify(esm) - return { - code, - map: null - } - } else { - return { - code: trimDebugMessage(esm, dependencies.map(viteProjectPath)), - map: null, - } - } - } catch (e) { - if (e instanceof Error && e.message.includes('-- NO MAIN')) { - const message = `${viteProjectPath( - pathname, - ) - }: NO MAIN.elm file is requested to transform by vite.Probably, this file is just a depending module` - throw message - } else { - if (isBuild) { - try { - let output = ElmErrorJson.parse(e.message) - console.error(`❗️ Elm Land build failed: `) - console.error('') - console.error(ElmErrorJson.toColoredTerminalOutput(output)) - console.error('') - return process.exit(1) - } catch (e) { - throw e - } - } else { - let elmError = ElmErrorJson.parse(e.message) - lastErrorSent = elmError - server.ws.send('elm:error', { - error: ElmErrorJson.toColoredHtmlOutput(elmError) - }) - - return { - code: `export const Elm = new Proxy({}, () => ({ init: () => { } }))`, - map: null - } - } - } - } finally { - releaseLock() - } - }, - } -} - -module.exports = { - plugin -} diff --git a/projects/cli/src/vite-plugins/elm/mutex.js b/projects/cli/src/vite-plugins/elm/mutex.js deleted file mode 100644 index e6bd324f..00000000 --- a/projects/cli/src/vite-plugins/elm/mutex.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * This approach comes from snowpack-plugin-elm by Marc Walter - * https://github.com/marc136/snowpack-plugin-elm - * - * To avoid gets an error: "It looks like some of the information cached in elm-stuff/ has been corrupted." from Elm compiler - * Elm compiler uses elm-stuff dir for cache which is expected not to be touched by other thread's compilation - */ -const queue = [] -let locked = false - -const acquireLock = async () => { - await new Promise((resolve) => { - if (!locked) { - resolve() - return - } - queue.push(resolve) - }) - - locked = true - - return () => { - let shifted = queue.shift() - if (shifted) { shifted() } - if (queue.length === 0) { - locked = false - } - } -} - -module.exports = { - acquireLock -} \ No newline at end of file diff --git a/projects/cli/src/vite-plugins/typescript/index.js b/projects/cli/src/vite-plugins/typescript/index.js index 5c260bf0..f11221bc 100644 --- a/projects/cli/src/vite-plugins/typescript/index.js +++ b/projects/cli/src/vite-plugins/typescript/index.js @@ -1,21 +1,25 @@ -const path = require('path') -const fs = require('fs') -const ChildProcess = require('child_process') -const { Terminal, Utils } = require('../../commands/_utils.js') -const { Files } = require('../../files.js') +import { join } from 'path' +import { existsSync } from 'fs' +import { spawn } from 'child_process' +import { Terminal, Utils } from '../../commands/_utils.js' +import { Files } from '../../files.js' +import path from 'path' +import url from 'url' + +let __dirname = path.dirname(url.fileURLToPath(import.meta.url)) // Here's where we'll expect to find the Typescript binary installed const tscPaths = { // When locally installed with `npm install -D elm-land` // ✅ Tested with npm install -D, yarn, pnpm i - local: path.join(__dirname, '..', '..', '..', '..', 'typescript', 'bin', 'tsc'), + local: join(__dirname, '..', '..', '..', '..', 'typescript', 'bin', 'tsc'), // When globally installed with `npm install -g elm-land` // ✅ Tested with npm install -g, yarn, pnpm - global: path.join(__dirname, '..', '..', '..', 'node_modules', '.bin', 'tsc'), + global: join(__dirname, '..', '..', '..', 'node_modules', '.bin', 'tsc'), } const pathToTsc = - fs.existsSync(tscPaths.global) + existsSync(tscPaths.global) ? tscPaths.global : tscPaths.local @@ -29,7 +33,7 @@ const parseImportId = (id) => { } } -const plugin = () => { +export const plugin = () => { return { name: 'vite-plugin-typescript', enforce: 'pre', @@ -72,7 +76,7 @@ const formatTypeScriptError = (str) => { } const checkForTypeScriptInteropFile = () => - Files.exists(path.join(process.cwd(), 'src', 'interop.ts')) + Files.exists(join(process.cwd(), 'src', 'interop.ts')) const handleUnexpectedTypeScriptError = (reject) => (err) => { if (err.code === 'ENOENT') { @@ -83,11 +87,11 @@ const handleUnexpectedTypeScriptError = (reject) => (err) => { } const spawnNewTypeScriptBuild = () => - ChildProcess.spawn(pathToTsc, argsForTypeScript()) + spawn(pathToTsc, argsForTypeScript()) const argsForTypeScript = () => { - const pathToTsConfigFile = path.join(process.cwd(), 'tsconfig.json') - const hasTsConfigFile = fs.existsSync(pathToTsConfigFile) + const pathToTsConfigFile = join(process.cwd(), 'tsconfig.json') + const hasTsConfigFile = existsSync(pathToTsConfigFile) if (hasTsConfigFile) { return ['--project', pathToTsConfigFile] @@ -130,14 +134,14 @@ const reportTypeScriptErrors = async () => { // Used during `elm-land build` to report errors // to users via the terminal (in full color!) -const verifyTypescriptCompiles = async (mode) => { +export const verifyTypescriptCompiles = async (mode) => { const hasInteropTs = await checkForTypeScriptInteropFile() if (hasInteropTs) { return new Promise((resolve, reject) => { console.info('\n' + Utils.intro.info(`is compiling ${Terminal.cyan('src/interop.ts')}...`)) - let tsc = ChildProcess.spawn(pathToTsc, argsForTypeScript(), { stdio: 'inherit' }) + let tsc = spawn(pathToTsc, argsForTypeScript(), { stdio: 'inherit' }) tsc.on('error', handleUnexpectedTypeScriptError(reject)) tsc.on('close', (code) => { @@ -151,9 +155,4 @@ const verifyTypescriptCompiles = async (mode) => { } else { return true } -} - -module.exports = { - plugin, - verifyTypescriptCompiles } \ No newline at end of file diff --git a/projects/cli/tests/01-basic.bats b/projects/cli/tests/01-basic.bats index d3c7b5a4..aca280fa 100644 --- a/projects/cli/tests/01-basic.bats +++ b/projects/cli/tests/01-basic.bats @@ -9,5 +9,5 @@ load helpers run elm-land banana expectToFail expectOutputContains "couldn't find" - expectOutputContains "Here are the available commands" + expectOutputContains "Commonly used commands" } \ No newline at end of file diff --git a/projects/cli/tests/09-elm-binary.bats b/projects/cli/tests/09-elm-binary.bats index 45c7f6e6..45b4bf20 100644 --- a/projects/cli/tests/09-elm-binary.bats +++ b/projects/cli/tests/09-elm-binary.bats @@ -41,7 +41,7 @@ load helpers cp -r ../../examples/01-hello-world ../../examples/01-local-hello cd ../../examples/01-local-hello - echo '{ "dependencies": { "elm-land": "file:../../projects/cli/elm-land-0.19.5.tgz" } }' > package.json + echo '{ "dependencies": { "elm-land": "file:../../projects/cli/elm-land-0.20.0.tgz" } }' > package.json npm install run npx elm-land build @@ -59,7 +59,7 @@ load helpers cp -r ../../examples/01-hello-world ../../examples/01-local-hello cd ../../examples/01-local-hello - echo '{ "dependencies": { "elm-land": "file:../../projects/cli/elm-land-0.19.5.tgz" } }' > package.json + echo '{ "dependencies": { "elm-land": "file:../../projects/cli/elm-land-0.20.0.tgz" } }' > package.json npm install -g yarn yarn @@ -78,7 +78,7 @@ load helpers cp -r ../../examples/01-hello-world ../../examples/01-local-hello cd ../../examples/01-local-hello - echo '{ "dependencies": { "elm-land": "file:../../projects/cli/elm-land-0.19.5.tgz" } }' > package.json + echo '{ "dependencies": { "elm-land": "file:../../projects/cli/elm-land-0.20.0.tgz" } }' > package.json npm install -g pnpm pnpm install diff --git a/projects/graphql/README.md b/projects/graphql/README.md index 40a60726..33296866 100644 --- a/projects/graphql/README.md +++ b/projects/graphql/README.md @@ -12,7 +12,7 @@ If you're excited to try things out– come join the [Elm Land Discord](https:// ## Installation -If you have [Node.js v16+](https://nodejs.org) installed, you can install the `elm-land` CLI via NPM. +If you have [Node.js v18+](https://nodejs.org) installed, you can install the `elm-land` CLI via NPM. Running the `elm-land graphql` command will automatically download this separate package, which includes dependencies on `graphql` @@ -23,7 +23,7 @@ npm install -g elm-land@latest ```txt $ elm-land graphql -🌈 Elm Land (v0.19.5) wants to add a plugin! +🌈 Elm Land (v0.20.0) wants to add a plugin! ⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺ To use the `@elm-land/graphql` plugin, I'll need to install the NPM package and add a bit of JSON @@ -35,14 +35,14 @@ $ elm-land graphql ```txt $ elm-land graphql build -🌈 Elm Land (v0.19.5) successfully generated Elm files +🌈 Elm Land (v0.20.0) successfully generated Elm files ⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺ ``` ```txt $ elm-land graphql watch -🌈 Elm Land (v0.19.5) is watching "./graphql/*" for changes... +🌈 Elm Land (v0.20.0) is watching "./graphql/*" for changes... ⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺⎺ ``` @@ -55,7 +55,7 @@ Here’s what running the CLI looks like when there’s no schema provided: ``` $ elm-land graphql build -🌈 Elm Land (v0.19.5) needs a GraphQL schema +🌈 Elm Land (v0.20.0) needs a GraphQL schema ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ You can provide one by customizing the "elm-land.json" file to include a "graphql.schema" field. diff --git a/projects/graphql/package-lock.json b/projects/graphql/package-lock.json index 89205720..01ab6250 100644 --- a/projects/graphql/package-lock.json +++ b/projects/graphql/package-lock.json @@ -1,12 +1,12 @@ { "name": "@elm-land/graphql", - "version": "0.19.5", + "version": "0.20.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@elm-land/graphql", - "version": "0.19.5", + "version": "0.20.0", "license": "ISC", "dependencies": { "graphql": "16.6.0" diff --git a/projects/graphql/package.json b/projects/graphql/package.json index 024e4552..e48576f4 100644 --- a/projects/graphql/package.json +++ b/projects/graphql/package.json @@ -1,6 +1,6 @@ { "name": "@elm-land/graphql", - "version": "0.19.5", + "version": "0.20.0", "description": "Generate Elm code from GraphQL files", "main": "src/index.js", "scripts": {