diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7b35590 --- /dev/null +++ b/.gitignore @@ -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 diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..d8aba6f --- /dev/null +++ b/default.nix @@ -0,0 +1,25 @@ +{ pkgs ? import {} +}: + +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 + ''; + +} diff --git a/elm.json b/elm.json new file mode 100644 index 0000000..019388f --- /dev/null +++ b/elm.json @@ -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" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..e7fc97e --- /dev/null +++ b/package.json @@ -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 ", + "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" + } +} diff --git a/scripts/import-channels-into-elasticsearch b/scripts/import-channels-into-elasticsearch index 23a31bf..2243fc7 100755 --- a/scripts/import-channels-into-elasticsearch +++ b/scripts/import-channels-into-elasticsearch @@ -27,13 +27,16 @@ def get_last_evaluation(channel): ) evaluations = [] for item in s3_result.get("CommonPrefixes"): - if not item : + if not item: continue prefix = item.get("Prefix") evaluation = prefix[len(f"{project}/{project_version}/{channel}"):] if evaluation.startswith("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( revisions_since_start=int(revisions_since_start), git_revision=git_revision, diff --git a/src/Main.elm b/src/Main.elm new file mode 100644 index 0000000..7c44a96 --- /dev/null +++ b/src/Main.elm @@ -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 + } diff --git a/src/assets/.gitkeep b/src/assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/assets/images/.gitkeep b/src/assets/images/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/assets/images/logo.png b/src/assets/images/logo.png new file mode 100644 index 0000000..dcb019c Binary files /dev/null and b/src/assets/images/logo.png differ diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..b78bafd --- /dev/null +++ b/src/index.html @@ -0,0 +1,11 @@ + + + + + + Elm hotloading dev environment + + + + + diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..7a86b21 --- /dev/null +++ b/src/index.js @@ -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; +} diff --git a/src/styles.scss b/src/styles.scss new file mode 100644 index 0000000..32b3611 --- /dev/null +++ b/src/styles.scss @@ -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; +} diff --git a/tests/Example.elm b/tests/Example.elm new file mode 100644 index 0000000..1bcd7b0 --- /dev/null +++ b/tests/Example.elm @@ -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 +-} +unitTest : Test +unitTest = + describe "simple unit test" + [ test "Inc adds one" <| + \() -> + update Inc (Model 0 "") + |> Tuple.first + |> .counter + |> Expect.equal 1 + ] + + +{-| See +-} +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 +-} +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" ] + ] diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..e87f41c --- /dev/null +++ b/webpack.config.js @@ -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" + ] + } + ] + } + }); +}