intial version of elm+webpack setup

This commit is contained in:
Rok Garbas 2020-03-28 05:09:01 +01:00
parent 5bccd8c0b2
commit 8077030139
Failed to generate hash of commit
14 changed files with 648 additions and 2 deletions

33
.gitignore vendored Normal file
View file

@ -0,0 +1,33 @@
# Logs
logs
*.log
.idea
.cache
npm-debug.log*
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directory
node_modules
# Optional npm cache directory
.npm
yarn.lock
# Optional REPL history
.node_repl_history
# elm-package generated files
elm-stuff/
# elm-repl generated files
repl-temp-*
.DS_Store
example/dist
ignore
dist
tests/VerifyExamples
package-lock.json

25
default.nix Normal file
View file

@ -0,0 +1,25 @@
{ pkgs ? import <nixpkgs> {}
}:
let
in pkgs.stdenv.mkDerivation {
name = "nixos-search";
src = pkgs.lib.cleanSource ./.;
buildInputs =
(with pkgs; [
nodejs
]) ++
(with pkgs.nodePackages; [
yarn
]) ++
(with pkgs.elmPackages; [
elm
elm-format
]);
shellHook = ''
export PATH=$PWD/node_modules/.bin:$PATH
'';
}

31
elm.json Normal file
View file

@ -0,0 +1,31 @@
{
"type": "application",
"source-directories": [
"src"
],
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"elm/browser": "1.0.2",
"elm/core": "1.0.4",
"elm/html": "1.0.0",
"elm/http": "2.0.0",
"elm/json": "1.1.3",
"elm/url": "1.0.0"
},
"indirect": {
"elm/bytes": "1.0.8",
"elm/file": "1.0.5",
"elm/time": "1.0.0",
"elm/virtual-dom": "1.0.2"
}
},
"test-dependencies": {
"direct": {
"elm-explorations/test": "1.2.2"
},
"indirect": {
"elm/random": "1.0.0"
}
}
}

45
package.json Normal file
View file

@ -0,0 +1,45 @@
{
"name": "nixos-search",
"version": "1.0.0",
"description": "Search NixOS packages and options.",
"main": "index.js",
"repository": "https://github.com/NixOS/nixos-search",
"author": "Rok Garbas <rok@garbas.si>",
"license": "MIT",
"scripts": {
"test": "elm-test",
"dev": "webpack-dev-server --hot --colors --port 3000",
"build": "webpack",
"prod": "webpack -p",
"analyse": "elm-analyse -s -p 3001 -o"
},
"devDependencies": {
"@babel/core": "^7.7.2",
"@babel/preset-env": "^7.7.1",
"babel-loader": "^8.0.6",
"clean-webpack-plugin": "^3.0.0",
"closure-webpack-plugin": "^2.0.1",
"copy-webpack-plugin": "^5.0.5",
"css-loader": "^3.2.0",
"elm-analyse": "^0.16.5",
"elm-hot-webpack-loader": "^1.1.6",
"elm-test": "^0.19.1-1",
"elm-webpack-loader": "^6.0.1",
"file-loader": "^6.0.0",
"google-closure-compiler": "^20200224.0.0",
"html-webpack-plugin": "^4.0.2",
"mini-css-extract-plugin": "^0.9.0",
"node-sass": "^4.13.1",
"resolve-url-loader": "^3.1.0",
"sass-loader": "^8.0.0",
"style-loader": "^1.0.0",
"url-loader": "^4.0.0",
"webpack": "^4.41.2",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.9.0",
"webpack-merge": "^4.2.2"
},
"dependencies": {
"purecss": "^1.0.1"
}
}

View file

@ -27,13 +27,16 @@ def get_last_evaluation(channel):
) )
evaluations = [] evaluations = []
for item in s3_result.get("CommonPrefixes"): for item in s3_result.get("CommonPrefixes"):
if not item : if not item:
continue continue
prefix = item.get("Prefix") prefix = item.get("Prefix")
evaluation = prefix[len(f"{project}/{project_version}/{channel}"):] evaluation = prefix[len(f"{project}/{project_version}/{channel}"):]
if evaluation.startswith("beta"): if evaluation.startswith("beta"):
evaluation = evaluation[len("beta"):] evaluation = evaluation[len("beta"):]
revisions_since_start, git_revision = evaluation.lstrip(".").rstrip("/").split(".") try:
revisions_since_start, git_revision = evaluation.lstrip(".").rstrip("/").split(".")
except:
continue
evaluations.append(dict( evaluations.append(dict(
revisions_since_start=int(revisions_since_start), revisions_since_start=int(revisions_since_start),
git_revision=git_revision, git_revision=git_revision,

216
src/Main.elm Normal file
View file

@ -0,0 +1,216 @@
port module Main exposing (main)
import Browser exposing (UrlRequest(..))
import Browser.Navigation as Nav exposing (Key)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick)
import Http exposing (Error(..))
import Json.Decode as Decode
import Url exposing (Url)
import Url.Parser as UrlParser exposing ((</>), Parser)
-- ---------------------------
-- PORTS
-- ---------------------------
port toJs : String -> Cmd msg
-- ---------------------------
-- MODEL
-- ---------------------------
type alias Model =
{ key : Key
, page : Page
}
init : Int -> Url -> Key -> ( Model, Cmd Msg )
init flags url key =
( { key = key, page = UrlParser.parse urlParser url |> Maybe.withDefault (Counter 0) }, Cmd.none )
type Page
= Counter Int
| Server String
-- ---------------------------
-- URL Parsing and Routing
-- ---------------------------
handleUrlRequest : Key -> UrlRequest -> Cmd msg
handleUrlRequest key urlRequest =
case urlRequest of
Internal url ->
Nav.pushUrl key (Url.toString url)
External url ->
Nav.load url
urlParser : Parser (Page -> msg) msg
urlParser =
UrlParser.oneOf
[ UrlParser.map Counter <| UrlParser.s "counter" </> UrlParser.int
, UrlParser.map Server <| UrlParser.s "server" </> UrlParser.string
, UrlParser.map (Server "") <| UrlParser.s "server"
]
-- ---------------------------
-- UPDATE
-- ---------------------------
type Msg
= OnUrlRequest UrlRequest
| OnUrlChange Url
| Inc
| TestServer
| OnServerResponse (Result Http.Error String)
update : Msg -> Model -> ( Model, Cmd Msg )
update message model =
case message of
OnUrlRequest urlRequest ->
( model, handleUrlRequest model.key urlRequest )
OnUrlChange url ->
( { model | page = UrlParser.parse urlParser url |> Maybe.withDefault model.page }, Cmd.none )
Inc ->
case model.page of
Counter x ->
let
xx =
x + 1
in
( { model | page = Counter xx }
, Nav.pushUrl model.key <| "/counter/" ++ String.fromInt xx
)
_ ->
( model, Cmd.none )
TestServer ->
let
expect =
Http.expectJson OnServerResponse (Decode.field "result" Decode.string)
in
( model
, Http.get { url = "/test", expect = expect }
)
OnServerResponse res ->
case res of
Ok serverMessage ->
( { model | page = Server serverMessage }, Cmd.none )
Err err ->
( { model | page = Server <| "Error: " ++ httpErrorToString err }, Cmd.none )
httpErrorToString : Http.Error -> String
httpErrorToString err =
case err of
BadUrl _ ->
"BadUrl"
Timeout ->
"Timeout"
NetworkError ->
"NetworkError"
BadStatus _ ->
"BadStatus"
BadBody s ->
"BadBody: " ++ s
-- ---------------------------
-- VIEW
-- ---------------------------
view : Model -> Html Msg
view model =
div [ class "container" ]
[ header []
[ img [ src "/images/logo.png" ] []
, h1 [] [ text "Elm 0.19 Webpack Starter, with hot-reloading" ]
]
, case model.page of
Counter counter ->
counterPage counter
Server serverMessage ->
serverPage serverMessage
, p []
[ text "And now don't forget to add a star to the Github repo "
, a [ href "https://github.com/simonh1000/elm-webpack-starter" ] [ text "elm-webpack-starter" ]
]
]
counterPage counter =
div [ class "pure-u-1-3" ]
[ a [ href "/server/" ] [ text "Switch to server" ]
, p [] [ text "Click on the button below to increment the state." ]
, button
[ class "pure-button pure-button-primary"
, onClick Inc
]
[ text "+ 1" ]
, text <| String.fromInt counter
, p [] [ text "Then make a change to the source code and see how the state is retained after you recompile." ]
]
serverPage serverMessage =
div [ class "pure-u-1-3" ]
[ a [ href "/counter/1" ] [ text "Switch to counter" ]
, p [] [ text "Test the dev server" ]
, button
[ class "pure-button pure-button-primary"
, onClick TestServer
]
[ text "ping dev server" ]
, text serverMessage
]
-- ---------------------------
-- MAIN
-- ---------------------------
main : Program Int Model Msg
main =
Browser.application
{ init = init
, update = update
, view =
\m ->
{ title = "Elm 0.19 starter"
, body = [ view m ]
}
, subscriptions = \_ -> Sub.none
, onUrlRequest = OnUrlRequest
, onUrlChange = OnUrlChange
}

0
src/assets/.gitkeep Normal file
View file

View file

BIN
src/assets/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

11
src/index.html Normal file
View file

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Elm hotloading dev environment</title>
</head>
<body>
</body>
</html>

15
src/index.js Normal file
View file

@ -0,0 +1,15 @@
'use strict';
require("./styles.scss");
const {Elm} = require('./Main');
var app = Elm.Main.init({flags: 6});
app.ports.toJs.subscribe(data => {
console.log(data);
})
// Use ES2015 syntax and let Babel compile it for you
var testFn = (inp) => {
let a = inp + 1;
return a;
}

30
src/styles.scss Normal file
View file

@ -0,0 +1,30 @@
@import '~purecss/build/pure.css';
body {
background-color: #ddd;
margin-top: 20px;
}
.container {
max-width: 800px;
margin-left: auto;
margin-right: auto;
}
header {
display: flex;
align-items: center;
img {
width: 60px;
margin-right: 30px;
}
}
button {
margin-right: 15px;
}
.logo {
background: url('images/logo.png');
width: 60px;
height: 60px;
background-size: cover;
}

52
tests/Example.elm Normal file
View file

@ -0,0 +1,52 @@
module Example exposing (fuzzTest, unitTest, viewTest)
import Expect exposing (Expectation)
import Fuzz exposing (Fuzzer, int, list, string)
import Main exposing (..)
import Test exposing (..)
import Test.Html.Query as Query
import Test.Html.Selector exposing (tag, text)
{-| See <https://github.com/elm-community/elm-test>
-}
unitTest : Test
unitTest =
describe "simple unit test"
[ test "Inc adds one" <|
\() ->
update Inc (Model 0 "")
|> Tuple.first
|> .counter
|> Expect.equal 1
]
{-| See <https://github.com/elm-community/elm-test>
-}
fuzzTest : Test
fuzzTest =
describe "simple fuzz test"
[ fuzz int "Inc ALWAYS adds one" <|
\ct ->
update Inc (Model ct "")
|> Tuple.first
|> .counter
|> Expect.equal (ct + 1)
]
{-| see <https://github.com/eeue56/elm-html-test>
-}
viewTest : Test
viewTest =
describe "Testing view function"
[ test "Button has the expected text" <|
\() ->
Model 0 ""
|> view
|> Query.fromHtml
|> Query.findAll [ tag "button" ]
|> Query.first
|> Query.has [ text "+ 1" ]
]

185
webpack.config.js Normal file
View file

@ -0,0 +1,185 @@
const path = require("path");
const webpack = require("webpack");
const merge = require("webpack-merge");
const ClosurePlugin = require('closure-webpack-plugin');
const CopyWebpackPlugin = require("copy-webpack-plugin");
const HTMLWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
// to extract the css as a separate file
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
var MODE =
process.env.npm_lifecycle_event === "prod" ? "production" : "development";
var withDebug = !process.env["npm_config_nodebug"] && MODE == "development";
// this may help for Yarn users
// var withDebug = !npmParams.includes("--nodebug");
console.log('\x1b[36m%s\x1b[0m', `** elm-webpack-starter: mode "${MODE}", withDebug: ${withDebug}\n`);
var common = {
mode: MODE,
entry: "./src/index.js",
output: {
path: path.join(__dirname, "dist"),
publicPath: "/",
// FIXME webpack -p automatically adds hash when building for production
filename: MODE == "production" ? "[name]-[hash].js" : "index.js"
},
plugins: [
new HTMLWebpackPlugin({
// Use this template to get basic responsive meta tags
template: "src/index.html",
// inject details of output file at end of body
inject: "body"
})
],
resolve: {
modules: [path.join(__dirname, "src"), "node_modules"],
extensions: [".js", ".elm", ".scss", ".png"]
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.scss$/,
exclude: [/elm-stuff/, /node_modules/],
// see https://github.com/webpack-contrib/css-loader#url
loaders: ["style-loader", "css-loader?url=false", "sass-loader"]
},
{
test: /\.css$/,
exclude: [/elm-stuff/, /node_modules/],
loaders: ["style-loader", "css-loader?url=false"]
},
{
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
exclude: [/elm-stuff/, /node_modules/],
loader: "url-loader",
options: {
limit: 10000,
mimetype: "application/font-woff"
}
},
{
test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
exclude: [/elm-stuff/, /node_modules/],
loader: "file-loader"
},
{
test: /\.(jpe?g|png|gif|svg)$/i,
exclude: [/elm-stuff/, /node_modules/],
loader: "file-loader"
}
]
}
};
if (MODE === "development") {
module.exports = merge(common, {
plugins: [
// Suggested for hot-loading
new webpack.NamedModulesPlugin(),
// Prevents compilation errors causing the hot loader to lose state
new webpack.NoEmitOnErrorsPlugin()
],
module: {
rules: [
{
test: /\.elm$/,
exclude: [/elm-stuff/, /node_modules/],
use: [
{ loader: "elm-hot-webpack-loader" },
{
loader: "elm-webpack-loader",
options: {
// add Elm's debug overlay to output
debug: withDebug,
//
forceWatch: true
}
}
]
}
]
},
devServer: {
inline: true,
stats: "errors-only",
contentBase: path.join(__dirname, "src/assets"),
historyApiFallback: true,
// feel free to delete this section if you don't need anything like this
before(app) {
// on port 3000
app.get("/test", function(req, res) {
res.json({ result: "OK" });
});
}
}
});
}
if (MODE === "production") {
module.exports = merge(common, {
//optimization: {
// minimizer: [
// new ClosurePlugin({mode: 'STANDARD'}, {})
// ]
//},
plugins: [
// Delete everything from output-path (/dist) and report to user
new CleanWebpackPlugin({
root: __dirname,
exclude: [],
verbose: true,
dry: false
}),
// Copy static assets
new CopyWebpackPlugin([
{
from: "src/assets"
}
]),
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: "[name]-[hash].css"
})
],
module: {
rules: [
{
test: /\.elm$/,
exclude: [/elm-stuff/, /node_modules/],
use: {
loader: "elm-webpack-loader",
options: {
optimize: true
}
}
},
{
test: /\.css$/,
exclude: [/elm-stuff/, /node_modules/],
loaders: [
MiniCssExtractPlugin.loader,
"css-loader?url=false"
]
},
{
test: /\.scss$/,
exclude: [/elm-stuff/, /node_modules/],
loaders: [
MiniCssExtractPlugin.loader,
"css-loader?url=false",
"sass-loader"
]
}
]
}
});
}