5

I would like to write specific components of my frontend using Purescript's Halogen.

For example, I would like to create a registration form using Halogen. It would look something like below:

module RegistrationForm where

import Prelude

...

-- | The state of the application
newtype State = State { email :: String, password :: String }

derive instance genericState :: Generic State
instance showState :: Show State where show = gShow
instance eqState :: Eq State where eq = gEq

initialState :: State
initialState = State { email: "", password: "" }

-- | Inputs to the state machine
data Input a = FormSubmit a
             | UpdateEmail String a
             | UpdatePassword String a
             | NoAction a

type AppEffects eff = HalogenEffects (ajax :: AJAX, console :: CONSOLE | eff)

ui :: forall eff . Component State Input (Aff (AppEffects eff))
ui = component render eval
  where
    render :: Render State Input
    render (State state) = H.div_
        [ H.h1_ [ H.text "Register" ]
        , ...
        ]

    eval :: Eval Input State Input (Aff (AppEffects eff))
    eval (NoAction next) = pure next
    eval (FormSubmit next) = do
        ...
    eval (UpdateEmail email next) = do
        ...
    eval (UpdatePassword password next) = do
        ...

runRegistrationForm :: Eff (AppEffects ()) Unit
runRegistrationForm = runAff throwException (const (pure unit)) $ do
    { node: node, driver: driver } <- runUI ui initialState
    appendTo ".registration" node

Similarly, I have a LoginForm module that handles logging a user into the application.

I'm wondering how to go about organizing my source code, building my source code, and calling my Purescript code from Javascript?

Currently, my source code is organized like the following:

$ cd my-application/
$ tree
.
├── bower.json
├── README.md
├── site/
│   └── index.html
├── src/
│   ├── LoginForm.purs
│   └── RegistrationForm.purs
└── test/
    └── Test.purs

However, since I don't have a Main.purs, I can't do any of the following to build my source code:

$ pulp build --to site/app.js
$ pulp browserify --to site/app.js
$ pulp server

It would be nice to be able to build my purescript code into logical javascript files. For instance, src/LoginForm.purs could be built as site/loginForm.js, and src/RegistrationForm.purs could be built as site/registrationForm.js.

Then, I could include loginForm.js and registrationForm.js in my actual html pages as need-be.

illabout
  • 3,517
  • 1
  • 18
  • 39

2 Answers2

2

Pulp doesn't really cover this use case, it's only intended for apps where there is a single Main.

I'd suggest using a gulp setup to achieve this, using a gulpfile something like this:

"use strict";

var gulp = require("gulp"),
    purescript = require("gulp-purescript"),
    webpack = require("webpack-stream");

var sources = [
  "src/**/*.purs",
  "bower_components/purescript-*/src/**/*.purs",
];

var foreigns = [
  "src/**/*.js",
  "bower_components/purescript-*/src/**/*.js"
];

gulp.task("make", function() {
  return purescript.psc({
    src: sources,
    ffi: foreigns
  });
});

var mkBundleTask = function (name, main) {

  gulp.task("prebundle-" + name, ["make"], function() {
    return purescript.pscBundle({
      src: "output/**/*.js",
      output: "tmp/js/" + name + ".js",
      module: main,
      main: main
    });
  });

  gulp.task("bundle-" + name, ["prebundle-" + name], function () {
    return gulp.src("tmp/js/" + name + ".js")
      .pipe(webpack({
        resolve: { modulesDirectories: ["node_modules"] },
        output: { filename: name + ".js" }
      }))
      .pipe(gulp.dest("site/js"));
  });

  return "bundle-" + name;
};

gulp.task("bundle", [
  mkBundleTask("loginForm", "LoginForm"),
  mkBundleTask("registrationForm", "RegistrationForm")
]);

gulp.task("default", ["bundle"]);

That might not be quite right, but I extracted it from how we do things with SlamData so it's definitely along the right lines.

gb.
  • 4,629
  • 1
  • 20
  • 19
2

This is possible with pulp, using the --to and --main options:

pulp build --main LoginForm -O --to site/loginForm.js
pulp build --main RegistrationForm -O --to site/registrationForm.js

Of course, the LoginForm and RegistrationForm modules will both need to export a value called main for this to work.

hdgarrood
  • 2,141
  • 16
  • 23
  • The problem with this solution is that requires you to run `pulp` twice. I'll need to have an additional `gulpfile` or `Makefile` to specify all the different invocations of `pulp`. I might as well just use `gulp`. – illabout Nov 26 '15 at 04:38
  • 2
    The normal approach here is to put it inside "scripts" in package.json. This solution does have the clear advantage of being two lines rather than ~40. – hdgarrood Nov 26 '15 at 04:43
  • I'm not very familiar with the Javascript ecosystem. Could you edit your answer and add a sample `package.json` showing what it would look like? Or even just add a link to a page describing it? – illabout Nov 26 '15 at 04:48
  • 2
    Sure, here's Prelude: https://github.com/purescript/purescript-prelude/blob/master/package.json – hdgarrood Nov 26 '15 at 04:53
  • All of the purescript core libraries use pulp, and most of the non-core ones do too, so there are plenty of examples to be found on GitHub. – hdgarrood Nov 26 '15 at 04:53
  • So, if I add the two lines from your answer to `scripts.build` in `package.json`, will they automatically get run when I do a `pulp build`? – illabout Nov 27 '15 at 01:55
  • No; pulp doesn't read package.json. In this case, your build command would become `npm run build`. – hdgarrood Nov 27 '15 at 17:15
  • So I guess I still have to use an external build tool--npm? Pulp can't do it alone? – illabout Nov 28 '15 at 05:32
  • Also, if I go the `npm` route, what would happen if I run `pulp server`? Would it serve both my loginform.js and registrationform.js? – illabout Nov 28 '15 at 05:34
  • 1
    Pulp can't do it alone, no (but npm isn't a build tool). I don't use pulp server so I don't know about that. – hdgarrood Nov 29 '15 at 11:03