├── .gitignore ├── src ├── static │ ├── img │ │ ├── elm.jpg │ │ ├── favicon.ico │ │ └── elm-bootstrap.svg │ ├── index.js │ ├── index.html │ └── bootstrap.scss └── elm │ ├── Components │ └── Hello.elm │ ├── Stylesheets.elm │ ├── MainCss.elm │ ├── HomePage.elm │ ├── Main.elm │ ├── RouteHelper.elm │ ├── Router.elm │ └── Navbar.elm ├── postcss.config.js ├── elm-package.json ├── package.json ├── README.md └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | elm-stuff/ 2 | node_modules/ 3 | tmp/ 4 | dist/ 5 | -------------------------------------------------------------------------------- /src/static/img/elm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiwhiz/elm-bootstrap-webpack-starter/HEAD/src/static/img/elm.jpg -------------------------------------------------------------------------------- /src/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiwhiz/elm-bootstrap-webpack-starter/HEAD/src/static/img/favicon.ico -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('autoprefixer')( { browsers: ['last 2 versions'] } ) 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /src/static/index.js: -------------------------------------------------------------------------------- 1 | // pull in desired CSS/SASS files 2 | require('./bootstrap') 3 | require('../elm/Stylesheets') 4 | 5 | // inject bundled Elm app into div#main 6 | var Elm = require( '../elm/Main' ); 7 | Elm.Main.embed( document.getElementById( 'main' ) ); 8 | -------------------------------------------------------------------------------- /src/elm/Components/Hello.elm: -------------------------------------------------------------------------------- 1 | module Components.Hello exposing (..) 2 | 3 | import Html exposing (..) 4 | import Html.Attributes exposing (..) 5 | import String 6 | 7 | 8 | -- hello component 9 | 10 | 11 | hello : Int -> Html a 12 | hello model = 13 | div 14 | [ class "h1" ] 15 | [ text ("Hello, Elm" ++ ("!" |> String.repeat model)) ] 16 | -------------------------------------------------------------------------------- /src/elm/Stylesheets.elm: -------------------------------------------------------------------------------- 1 | port module Stylesheets exposing (..) 2 | 3 | import Css.File exposing (..) 4 | import MainCss as Main 5 | 6 | 7 | port files : CssFileStructure -> Cmd msg 8 | 9 | 10 | cssFiles : CssFileStructure 11 | cssFiles = 12 | toFileStructure [ ( "main.css", compile [ Main.css ] ) ] 13 | 14 | 15 | main : CssCompilerProgram 16 | main = 17 | Css.File.compiler files cssFiles 18 | -------------------------------------------------------------------------------- /src/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | elm-webpack-starter 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /src/static/bootstrap.scss: -------------------------------------------------------------------------------- 1 | 2 | // Bootstrap overrides 3 | // 4 | // Copy variables from `_variables.scss` to this file to override default values 5 | // without modifying source files. 6 | 7 | // Reassign color vars to semantic color scheme 8 | $brand-primary: rgb(86,63,122); 9 | 10 | $navbar-light-color: rgba($brand-primary,.75); 11 | $navbar-light-hover-color: rgba($brand-primary,1.25); 12 | $navbar-light-active-color: rgba($brand-primary,1); 13 | 14 | @import '~bootstrap/scss/bootstrap.scss'; 15 | 16 | $fa-font-path: "~font-awesome/fonts"; 17 | @import '~font-awesome/scss/font-awesome.scss'; 18 | -------------------------------------------------------------------------------- /src/elm/MainCss.elm: -------------------------------------------------------------------------------- 1 | module MainCss exposing (..) 2 | 3 | import Html exposing (Attribute) 4 | import Html.Attributes 5 | import Css exposing (..) 6 | import Css.Elements exposing (body) 7 | import Css.Namespace exposing (namespace) 8 | 9 | 10 | type CssClasses 11 | = NavBar 12 | | Jumbotron 13 | | BrandLogo 14 | 15 | 16 | type CssIds 17 | = Page 18 | 19 | 20 | css : Stylesheet 21 | css = 22 | (stylesheet << namespace "ebws") 23 | [ body 24 | [ minHeight (Css.rem 75) 25 | , paddingTop (Css.rem 4.5) 26 | ] 27 | , class Jumbotron 28 | [ textAlign center 29 | ] 30 | , class BrandLogo 31 | [ width (px 30) 32 | ] 33 | ] 34 | 35 | 36 | styles : List Css.Mixin -> Attribute msg 37 | styles = 38 | Css.asPairs >> Html.Attributes.style 39 | -------------------------------------------------------------------------------- /elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.0", 3 | "summary": "Example using `elm-webpack-loader`.", 4 | "repository": "https://github.com/jiwhiz/elm-bootstrap-webpack-starter.git", 5 | "license": "MIT", 6 | "source-directories": [ 7 | "src/elm" 8 | ], 9 | "exposed-modules": [], 10 | "dependencies": { 11 | "elm-lang/core": "5.0.0 <= v < 6.0.0", 12 | "elm-lang/html": "2.0.0 <= v < 3.0.0", 13 | "elm-lang/http": "1.0.0 <= v < 2.0.0", 14 | "elm-lang/navigation": "2.1.0 <= v < 3.0.0", 15 | "evancz/elm-markdown": "3.0.1 <= v < 4.0.0", 16 | "evancz/url-parser": "2.0.1 <= v < 3.0.0", 17 | "Fresheyeball/elm-font-awesome": "2.0.2 <= v < 3.0.0", 18 | "rtfeldman/elm-css": "8.1.0 <= v < 9.0.0", 19 | "rtfeldman/elm-css-helpers": "2.0.1 <= v < 3.0.0", 20 | "rundis/elm-bootstrap": "2.0.0 <= v < 3.0.0" 21 | }, 22 | "elm-version": "0.18.0 <= v < 0.19.0" 23 | } 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "elm-bootstrap-webpack-starter", 3 | "description": "Webpack setup for writing Elm apps with elm-bootstrap", 4 | "version": "0.1.0", 5 | "license": "MIT", 6 | "author": "Yuan Ji", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/jiwhiz/elm-bootstrap-webpack-starter" 10 | }, 11 | "scripts": { 12 | "start": "webpack-dev-server --hot --inline", 13 | "build": "rimraf dist && webpack ", 14 | "reinstall": "npm i rimraf && rimraf elm-stuff && rimraf node_modules && npm i && elm package install", 15 | "local": "http-server dist" 16 | }, 17 | "devDependencies": { 18 | "autoprefixer": "^6.3.6", 19 | "bootstrap": "4.0.0-alpha.6", 20 | "copy-webpack-plugin": "^4.0.1", 21 | "css-loader": "^0.27.2", 22 | "elm": "^0.18.0", 23 | "elm-css-webpack-loader": "^3.1.0", 24 | "elm-hot-loader": "^0.5.4", 25 | "elm-webpack-loader": "^4.2.0", 26 | "extract-text-webpack-plugin": "^2.1.0", 27 | "file-loader": "^0.10.1", 28 | "font-awesome": "^4.7.0", 29 | "html-webpack-plugin": "^2.28.0", 30 | "http-server": "^0.9.0", 31 | "node-sass": "^4.2.0", 32 | "postcss-loader": "^1.1.1", 33 | "rimraf": "^2.5.2", 34 | "sass-loader": "^6.0.0", 35 | "style-loader": "^0.13.1", 36 | "url-loader": "^0.5.7", 37 | "webpack": "^2.2.1", 38 | "webpack-dev-server": "^2.4.1", 39 | "webpack-merge": "^3.0.0" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/elm/HomePage.elm: -------------------------------------------------------------------------------- 1 | module HomePage exposing (..) 2 | 3 | import Html exposing (..) 4 | import Html.Attributes exposing (..) 5 | import Html.Events exposing (onClick) 6 | import Bootstrap.Button as Button 7 | import Css 8 | import Html.CssHelpers 9 | import MainCss 10 | import Components.Hello exposing (hello) 11 | 12 | 13 | type Msg 14 | = Increment 15 | 16 | 17 | type alias Model = 18 | { count : Int 19 | } 20 | 21 | 22 | { id, class, classList } = 23 | Html.CssHelpers.withNamespace "ebws" 24 | 25 | 26 | init : Model 27 | init = 28 | { count = 0 29 | } 30 | 31 | 32 | update : Msg -> Model -> ( Model, Cmd Msg ) 33 | update msg model = 34 | ( { count = model.count + 1 }, Cmd.none ) 35 | 36 | 37 | view : Model -> Html Msg 38 | view model = 39 | div [ class [ MainCss.Jumbotron ] ] 40 | [ img 41 | [ src "static/img/elm.jpg" 42 | , MainCss.styles 43 | [ Css.width (Css.pct 33) 44 | , Css.border3 (Css.px 4) Css.solid (Css.hex "#337AB7") 45 | ] 46 | ] 47 | [] 48 | , hello model.count 49 | , p [] [ text ("Elm Bootstrap Webpack Starter") ] 50 | , Button.button 51 | [ Button.primary 52 | , Button.large 53 | , Button.attrs [ onClick Increment ] 54 | ] 55 | [ span [] [] 56 | , span [] [ text "FTW!" ] 57 | ] 58 | ] 59 | -------------------------------------------------------------------------------- /src/static/img/elm-bootstrap.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | 27 | 28 | 29 | 32 | 33 | 34 | 37 | 38 | 41 | 42 | 43 | 46 | 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # elm-bootstrap-webpack-starter 2 | 3 | 4 | ### About: 5 | A simple Webpack 2 setup for writing [Elm](http://elm-lang.org/) apps 6 | with [elm-bootstrap](http://elm-bootstrap.info/): 7 | 8 | * Dev server with live reloading, HMR 9 | * Support for CSS/SCSS (with Autoprefixer), image assets 10 | * Bundling and minification for deployment 11 | * Bootstrap 4 Template 12 | [fixed navbar example](https://v4-alpha.getbootstrap.com/examples/navbar-top-fixed) 13 | implemented by [elm-bootstrap Navbar](http://elm-bootstrap.info/navbar) 14 | * Use [elm-css](https://github.com/rtfeldman/elm-css) to generate css from elm code 15 | * Use [elm-css-webpack-loader](https://github.com/jiwhiz/elm-css-webpack-loader/tree/upgrade) 16 | to load Stylesheets.elm as css 17 | 18 | 19 | ### Install: 20 | Clone this repo into a new project folder, e.g. `my-elm-project`: 21 | ``` 22 | git clone https://github.com/jiwhiz/elm-bootstrap-webpack-starter my-elm-project 23 | cd my-elm-project 24 | ``` 25 | 26 | Re-initialize the project folder as your own repo: 27 | ``` 28 | rm -rf .git # on Windows: rmdir .git /s /q 29 | git init 30 | git add . 31 | git commit -m 'first commit' 32 | ``` 33 | 34 | Install all dependencies using the handy `reinstall` script: 35 | ``` 36 | npm run reinstall 37 | ``` 38 | *This does a clean (re)install of all npm and elm packages.* 39 | 40 | 41 | ### Serve locally: 42 | ``` 43 | npm start 44 | ``` 45 | * Access app at `http://localhost:8080/` 46 | * Get coding! The entry point file is `src/elm/Main.elm` 47 | * Browser will refresh automatically on any file changes.. 48 | 49 | 50 | ### Build & bundle for prod: 51 | ``` 52 | npm run build 53 | ``` 54 | 55 | * Files are saved into the `/dist` folder 56 | * To run locally, use `npm run local` 57 | 58 | 59 | ### Credits: 60 | 61 | * [elm-community/elm-webpack-starter](https://github.com/elm-community/elm-webpack-starter) 62 | * [rundis/albums](https://github.com/rundis/albums) 63 | * [ohanhi/elm-taco](https://github.com/ohanhi/elm-taco) 64 | -------------------------------------------------------------------------------- /src/elm/Main.elm: -------------------------------------------------------------------------------- 1 | module Main exposing (..) 2 | 3 | import Html exposing (..) 4 | import Html.CssHelpers 5 | import Navigation exposing (Location) 6 | import Bootstrap.Navbar 7 | import Router 8 | 9 | 10 | { id, class, classList } = 11 | Html.CssHelpers.withNamespace "ebws" 12 | 13 | 14 | type Msg 15 | = OnLocationChange Location 16 | | RouterMsg Router.Msg 17 | 18 | 19 | type alias Model = 20 | { routerModel : Router.Model 21 | } 22 | 23 | 24 | main : Program Never Model Msg 25 | main = 26 | Navigation.program OnLocationChange 27 | { init = init 28 | , view = view 29 | , update = update 30 | , subscriptions = subscriptions 31 | } 32 | 33 | 34 | init : Navigation.Location -> ( Model, Cmd Msg ) 35 | init location = 36 | let 37 | ( routerModel, routerCmd ) = 38 | Router.init location 39 | in 40 | ( { routerModel = routerModel 41 | } 42 | , routerCmd |> Cmd.map RouterMsg 43 | ) 44 | 45 | 46 | subscriptions : Model -> Sub Msg 47 | subscriptions model = 48 | Bootstrap.Navbar.subscriptions model.routerModel.navbarState Router.NavbarMsg |> Sub.map RouterMsg 49 | 50 | 51 | update : Msg -> Model -> ( Model, Cmd Msg ) 52 | update msg model = 53 | case msg of 54 | OnLocationChange location -> 55 | let 56 | ( routerModel, routerCmd ) = 57 | Router.update (Router.OnLocationChange location) model.routerModel 58 | in 59 | ( { model | routerModel = routerModel }, routerCmd |> Cmd.map RouterMsg ) 60 | 61 | RouterMsg routerMsg -> 62 | let 63 | ( routerModel, routerCmd ) = 64 | Router.update routerMsg model.routerModel 65 | in 66 | ( { model | routerModel = routerModel }, routerCmd |> Cmd.map RouterMsg ) 67 | 68 | 69 | view : Model -> Html Msg 70 | view model = 71 | div [] 72 | [ Router.view model.routerModel |> Html.map RouterMsg 73 | ] 74 | -------------------------------------------------------------------------------- /src/elm/RouteHelper.elm: -------------------------------------------------------------------------------- 1 | module RouteHelper exposing (..) 2 | 3 | import Html exposing (..) 4 | import Html.Attributes exposing (..) 5 | import Html.Events exposing (onClick) 6 | import UrlParser exposing (Parser, (), int, oneOf, s) 7 | import Navigation exposing (Location) 8 | import Html.Attributes exposing (href, attribute) 9 | import Html exposing (Html, Attribute, a) 10 | import Html.Events exposing (onWithOptions) 11 | import Bootstrap.Navbar 12 | import Json.Decode as Json 13 | 14 | 15 | type Route 16 | = HomeRoute 17 | | AboutRoute 18 | | NotFoundRoute 19 | 20 | 21 | routeParser : Parser (Route -> a) a 22 | routeParser = 23 | UrlParser.oneOf 24 | [ UrlParser.map HomeRoute (UrlParser.s "") 25 | , UrlParser.map AboutRoute (UrlParser.s "about") 26 | ] 27 | 28 | 29 | parseLocation : Navigation.Location -> Route 30 | parseLocation location = 31 | case (UrlParser.parsePath routeParser location) of 32 | Just route -> 33 | route 34 | 35 | Nothing -> 36 | NotFoundRoute 37 | 38 | 39 | encode : Route -> String 40 | encode route = 41 | case route of 42 | HomeRoute -> 43 | "/" 44 | 45 | AboutRoute -> 46 | "/about" 47 | 48 | NotFoundRoute -> 49 | "/" 50 | 51 | 52 | navigate : Route -> Cmd msg 53 | navigate route = 54 | Navigation.newUrl (encode route) 55 | 56 | 57 | linkTo : Route -> List (Attribute msg) -> List (Html msg) -> Html msg 58 | linkTo route attrs content = 59 | a ((linkAttrs route) ++ attrs) content 60 | 61 | 62 | linkAttrs : Route -> List (Attribute msg) 63 | linkAttrs route = 64 | let 65 | path = 66 | encode route 67 | in 68 | [ href path 69 | , attribute "data-navigate" path 70 | ] 71 | 72 | 73 | catchNavigationClicks : msg -> Attribute msg 74 | catchNavigationClicks message = 75 | onWithOptions "click" 76 | { stopPropagation = True 77 | , preventDefault = True 78 | } 79 | (Json.succeed message) 80 | 81 | 82 | pathDecoder : Json.Decoder String 83 | pathDecoder = 84 | Json.oneOf 85 | [ Json.at [ "data-navigate" ] Json.string 86 | , Json.at [ "parentElement" ] (Json.lazy (\_ -> pathDecoder)) 87 | , Json.fail "no path found for click" 88 | ] 89 | -------------------------------------------------------------------------------- /src/elm/Router.elm: -------------------------------------------------------------------------------- 1 | module Router exposing (..) 2 | 3 | import Html exposing (..) 4 | import Navigation exposing (Location) 5 | import Html exposing (Html, Attribute, a) 6 | import Bootstrap.Grid as Grid 7 | import Bootstrap.Navbar 8 | import HomePage 9 | import Navbar 10 | import RouteHelper exposing (..) 11 | 12 | 13 | type Msg 14 | = ChangeLocation String 15 | | OnLocationChange Location 16 | | NavbarMsg Bootstrap.Navbar.State 17 | | HomePageMsg HomePage.Msg 18 | 19 | 20 | type alias Model = 21 | { route : Route 22 | , navbarState : Bootstrap.Navbar.State 23 | , homePageModel : HomePage.Model 24 | } 25 | 26 | 27 | init : Navigation.Location -> ( Model, Cmd Msg ) 28 | init location = 29 | let 30 | currentRoute = 31 | parseLocation location 32 | 33 | ( navbarState, navbarCmd ) = 34 | Bootstrap.Navbar.initialState NavbarMsg 35 | in 36 | ( { route = currentRoute 37 | , navbarState = navbarState 38 | , homePageModel = HomePage.init 39 | } 40 | , navbarCmd 41 | ) 42 | 43 | 44 | update : Msg -> Model -> ( Model, Cmd Msg ) 45 | update msg model = 46 | case msg of 47 | ChangeLocation path -> 48 | ( model, Navigation.newUrl path ) 49 | 50 | OnLocationChange location -> 51 | let 52 | newRoute = 53 | parseLocation location 54 | in 55 | ( { model | route = newRoute }, Cmd.none ) 56 | 57 | NavbarMsg state -> 58 | ( { model | navbarState = state }, Cmd.none ) 59 | 60 | HomePageMsg homePageMsg -> 61 | updateHomePage homePageMsg model 62 | 63 | 64 | updateHomePage : HomePage.Msg -> Model -> ( Model, Cmd Msg ) 65 | updateHomePage msg model = 66 | let 67 | ( homePageModel, homePageCmd ) = 68 | HomePage.update msg model.homePageModel 69 | in 70 | ( { model | homePageModel = homePageModel }, homePageCmd |> Cmd.map HomePageMsg ) 71 | 72 | 73 | view : Model -> Html Msg 74 | view model = 75 | div [] 76 | [ Navbar.view model.navbarState model.route ChangeLocation NavbarMsg 77 | , page model 78 | ] 79 | 80 | 81 | page : Model -> Html Msg 82 | page model = 83 | Grid.container [] 84 | [ case model.route of 85 | HomeRoute -> 86 | Html.map HomePageMsg (HomePage.view model.homePageModel) 87 | 88 | AboutRoute -> 89 | text "About" 90 | 91 | NotFoundRoute -> 92 | text "Not Found" 93 | ] 94 | -------------------------------------------------------------------------------- /src/elm/Navbar.elm: -------------------------------------------------------------------------------- 1 | module Navbar exposing (..) 2 | 3 | import Html exposing (..) 4 | import Html.Attributes exposing (..) 5 | import Html.CssHelpers 6 | import Bootstrap.Button as Button 7 | import Bootstrap.Form.Input as Input 8 | import Bootstrap.Navbar as Navbar exposing (State) 9 | import FontAwesome.Web as Icon 10 | import MainCss 11 | import RouteHelper exposing (..) 12 | 13 | 14 | { id, class, classList } = 15 | Html.CssHelpers.withNamespace "ebws" 16 | 17 | 18 | view : State -> Route -> (String -> msg) -> (State -> msg) -> Html msg 19 | view state currentRoute changeLocationMsgTagger navbarMsgTagger = 20 | div [] 21 | [ Navbar.config navbarMsgTagger 22 | |> Navbar.withAnimation 23 | |> Navbar.collapseMedium 24 | |> Navbar.fixTop 25 | |> Navbar.brand 26 | [ href "/" ] 27 | [ img 28 | [ src "static/img/elm-bootstrap.svg" 29 | , class [ MainCss.BrandLogo ] 30 | ] 31 | [] 32 | , text " Project" 33 | ] 34 | |> Navbar.items 35 | [ routeToItemLink changeLocationMsgTagger currentRoute HomeRoute Icon.home " Home" 36 | , routeToItemLink changeLocationMsgTagger currentRoute AboutRoute Icon.info " About" 37 | , Navbar.dropdown 38 | { id = "mydropdown" 39 | , toggle = Navbar.dropdownToggle [] [ text "My dropdown" ] 40 | , items = 41 | [ Navbar.dropdownHeader [ text "Heading" ] 42 | , routeToDropdownItem changeLocationMsgTagger HomeRoute "Drop Item 1" 43 | , routeToDropdownItem changeLocationMsgTagger HomeRoute "Drop Item 2" 44 | , Navbar.dropdownDivider 45 | , routeToDropdownItem changeLocationMsgTagger HomeRoute "Drop Item 3" 46 | ] 47 | } 48 | ] 49 | |> Navbar.customItems 50 | [ Navbar.formItem [] 51 | [ Input.text [ Input.attrs <| [ placeholder "enter" ] ] 52 | , Button.button 53 | [ Button.outlineSuccess 54 | , Button.attrs [ Html.Attributes.class "ml-sm-2" ] 55 | ] 56 | [ text "Search" ] 57 | ] 58 | , Navbar.textItem [ Html.Attributes.class "muted ml-sm-2" ] [ text "Text" ] 59 | ] 60 | |> Navbar.view state 61 | ] 62 | 63 | 64 | routeToItemLink : (String -> msg) -> Route -> Route -> Html msg -> String -> Navbar.Item msg 65 | routeToItemLink changeLocationMsgTagger currentRoute linkedToRoute icon title = 66 | let 67 | path = 68 | encode linkedToRoute 69 | 70 | itemLink = 71 | if currentRoute == linkedToRoute then 72 | Navbar.itemLinkActive 73 | else 74 | Navbar.itemLink 75 | in 76 | itemLink 77 | [ href path 78 | , attribute "data-navigate" path 79 | , catchNavigationClicks (changeLocationMsgTagger path) 80 | ] 81 | [ icon, text title ] 82 | 83 | 84 | routeToDropdownItem : (String -> msg) -> Route -> String -> Navbar.DropdownItem msg 85 | routeToDropdownItem changeLocationMsgTagger linkedToRoute title = 86 | let 87 | path = 88 | encode linkedToRoute 89 | in 90 | Navbar.dropdownItem 91 | [ href path 92 | , attribute "data-navigate" path 93 | , catchNavigationClicks (changeLocationMsgTagger path) 94 | ] 95 | [ text title ] 96 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require( 'path' ); 2 | var webpack = require( 'webpack' ); 3 | var merge = require( 'webpack-merge' ); 4 | var HtmlWebpackPlugin = require( 'html-webpack-plugin' ); 5 | var autoprefixer = require( 'autoprefixer' ); 6 | var ExtractTextPlugin = require( 'extract-text-webpack-plugin' ); 7 | var CopyWebpackPlugin = require( 'copy-webpack-plugin' ); 8 | var entryPath = path.join( __dirname, 'src/static/index.js' ); 9 | var outputPath = path.join( __dirname, 'dist' ); 10 | 11 | console.log( 'WEBPACK GO!'); 12 | 13 | // determine build env 14 | var TARGET_ENV = process.env.npm_lifecycle_event === 'build' ? 'production' : 'development'; 15 | var outputFilename = TARGET_ENV === 'production' ? '[name]-[hash].js' : '[name].js' 16 | 17 | // common webpack config 18 | var commonConfig = { 19 | 20 | output: { 21 | path: outputPath, 22 | filename: `static/js/${outputFilename}`, 23 | publicPath: '/' 24 | }, 25 | 26 | resolve: { 27 | extensions: ['.js', '.elm', '.scss'] 28 | }, 29 | 30 | module: { 31 | rules: [ 32 | { 33 | test: /\.(eot|ttf|svg)$/, 34 | loader: 'file-loader' 35 | } 36 | ] 37 | }, 38 | 39 | plugins: [ 40 | new HtmlWebpackPlugin({ 41 | template: 'src/static/index.html', 42 | inject: 'body', 43 | filename: 'index.html' 44 | }) 45 | ] 46 | 47 | } 48 | 49 | // additional webpack settings for local env (when invoked by 'npm start') 50 | if ( TARGET_ENV === 'development' ) { 51 | console.log( 'Serving locally...'); 52 | 53 | module.exports = merge( commonConfig, { 54 | 55 | entry: [ 56 | 'webpack-dev-server/client?http://localhost:8080', 57 | entryPath 58 | ], 59 | 60 | devServer: { 61 | // serve index.html in place of 404 responses 62 | historyApiFallback: true, 63 | contentBase: './src', 64 | }, 65 | 66 | module: { 67 | rules: [ 68 | { 69 | test: /\.elm$/, 70 | exclude: [/elm-stuff/, /node_modules/, /Stylesheets\.elm$/], 71 | use: [ 72 | 'elm-hot-loader', 73 | 'elm-webpack-loader' 74 | ] 75 | }, 76 | { 77 | test: /Stylesheets\.elm$/, 78 | use: [ 79 | 'style-loader', 80 | 'css-loader', 81 | 'elm-css-webpack-loader' 82 | ] 83 | }, 84 | { 85 | test: /\.scss$/, 86 | use: [ 87 | 'style-loader', 88 | 'css-loader', 89 | 'postcss-loader', 90 | 'sass-loader' 91 | ] 92 | }, 93 | { 94 | test: /\.(woff|woff2)(\?v=[a-z0-9]\.[a-z0-9]\.[a-z0-9])?$/, 95 | loader: 'url-loader?limit=100000' 96 | } 97 | ] 98 | } 99 | 100 | }); 101 | } 102 | 103 | // additional webpack settings for prod env (when invoked via 'npm run build') 104 | if ( TARGET_ENV === 'production' ) { 105 | console.log( 'Building for prod...'); 106 | 107 | module.exports = merge( commonConfig, { 108 | 109 | entry: entryPath, 110 | 111 | module: { 112 | rules: [ 113 | { 114 | test: /\.elm$/, 115 | exclude: [/elm-stuff/, /node_modules/, /Stylesheets\.elm/], 116 | use: 'elm-webpack-loader' 117 | }, 118 | { 119 | test: /Stylesheets\.elm$/, 120 | use: ExtractTextPlugin.extract({ 121 | fallback: "style-loader", 122 | use: [ 123 | 'css-loader', 124 | 'elm-css-webpack-loader' 125 | ] 126 | }) 127 | }, 128 | { 129 | test: /\.scss$/, 130 | use: ExtractTextPlugin.extract({ 131 | fallback: 'style-loader', 132 | use: [ 133 | 'css-loader', 134 | 'postcss-loader', 135 | 'sass-loader' 136 | ] 137 | }) 138 | }, 139 | { 140 | test: /\.(woff|woff2)(\?v=[a-z0-9]\.[a-z0-9]\.[a-z0-9])?$/, 141 | loader: 'url-loader?limit=100000' 142 | } 143 | ] 144 | }, 145 | 146 | plugins: [ 147 | new CopyWebpackPlugin([ 148 | { 149 | from: 'src/static/img/', 150 | to: 'static/img/' 151 | } 152 | ]), 153 | 154 | new webpack.optimize.OccurrenceOrderPlugin(), 155 | 156 | // extract CSS into a separate file 157 | new ExtractTextPlugin( 'static/styles/[name]-[hash].css'), 158 | 159 | // minify & mangle JS/CSS 160 | new webpack.optimize.UglifyJsPlugin({ 161 | minimize: true, 162 | compressor: { warnings: false } 163 | // mangle: true 164 | }) 165 | ], 166 | 167 | }); 168 | } 169 | --------------------------------------------------------------------------------