TypeScript
hyperapp
parcel

hyperappをTypeScriptと共にお手軽にparcelで試す。

React+TypeScriptならcreate-react-appというツールがあって簡単に試せますけど
hyperapp+TypeScriptはどうなんでしょうか?
parcelを使うと簡単に試せそうだと思い、多少試行錯誤したら簡単にできたので纏めてみます。

先ずはparcelを導入してみます。

yarn global add parcel-bundler

では、プロジェクトを作るところから

mkdir hyperapp-ts-sample
cd hyperapp-ts-sample
npm --init

npm --initでpackage.jsonを作っておきます。

更に追加でローカルにバッケージを導入します

yarn add babel-preset-env hyperapp

babel-preset-env を入れないと実行時にCouldn't find preset "env" というエラーが出ます

また、今回の肝となりますが以下のようにtsconfigを作ります。

tsconfig.json
{
  "compilerOptions": {
    "strict": true,
    "jsx": "react",
    "jsxFactory": "h"
  }
}

parcelはindex.htmlをエントリーポイントとして指定できるみたいなので
以下のようにindexhtmlを作ります。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Counter</title>
    <link rel="stylesheet" href="src/counter.scss">
</head>
<body>
    <script src="src/counter.tsx"></script>
</body>
</html>

src配下にソースを置いていきます。ちなみにhtml内のファイル名と一致させていることを確認して下さい。
parcelがhtml内のtsファイル名から追いかけてくれます。
更にscssも直接指定できました。
マニュアルをろくに確認せずに使えてしまうparcelすごいっす。

src/counter.tsx
import { h, app } from "hyperapp"

const SECONDS = 5

const pad = (n: number) => (n < 10 ? "0" + n : n)

const humanizeTime = (t: number) => {
  const hours = (t / 3600) >> 0
  const minutes = ((t - hours * 3600) / 60) >> 0
  const seconds = (t - hours * 3600 - minutes * 60) >> 0
  return `${pad(minutes)}:${pad(seconds)}`
}

const state = {
  count: SECONDS,
  paused: true
}


const actions = {
  tick: () => (state: State, actions: Actions) => {
    if (state.count === 0) {
      actions.reset()
      actions.toggle()
    } else if (!state.paused) {
      actions.drop()
    }
  },
  drop: () => (state: State) => ({ count: state.count - 1 }),
  reset: () => ({ count: SECONDS }),
  toggle: () => (state: State) => ({ paused: !state.paused })
}

type State = typeof state;
type Actions = typeof actions;

const view = (state: State, actions: Actions) => (
  <main>
    <h1>{humanizeTime(state.count)}</h1>

    <button onclick={actions.toggle}>
      {state.paused ? "START" : "PAUSE"}
    </button>

    <button onclick={actions.reset}>RESET</button>
  </main>
)

const main = app(state, actions, view, document.body)

setInterval(main.tick, 1000)
src/counter.scss
body {
    align-items: center;
    background-color: #111;
    display: flex;
    font-family: Helvetica Neue, sans-serif;
    height: 100vh;
    justify-content: center;
    text-align: center;

    margin: 0;
    padding: 0;
  }

  h1 {
    color: #00caff;
    font-weight: 100;
    font-size: 9em;
    margin: 0;

    padding-bottom: 10px;
  }

  button {
    background: #111;
    border: 1px solid #00caff;
    color: #00caff;

    font-size: 1.5em;
    font-weight: 200;

    outline: none;
    width: 48%;
    height: 50px;

    letter-spacing: 1px;

    margin: 0;

    &:hover, &:disabled, &.paused {
      background: #00caff;
      color: #111;
    }
    &:active {
      outline: 2px solid #00caff;
    }
    &:focus {
      border: 1px solid #00caff;
    }
  }

  button + button {
    margin-left: 10px;
  }

あとは以下のコマンドを実行するだけです。

parcel index.html

試すにあたって詰まったところはtsconfig.jsonbabel-preset-envくらいでした。
create-react-appみたいなツールがなくても簡単にお試し環境は作れるって確認できて良かったです。

一応githubにコードを置いておきます。
https://github.com/m0a-mystudy/hyperapp-ts-sample

Parcel関連のドキュメントの翻訳を作成中です。
https://taiyaq.com/tags/Parcel