1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Elm+TypescriptにVSCodeのLinter設定してGitHubActionsでgithub.ioに公開したメモ

Last updated at Posted at 2021-05-09

概要

プログラミングElmを買った。
開発を試すための準備を行ったのでメモを残す。

ソースコード

やること

  • webpack5を試す
    • 試した結果、webpack4のときと違いなし
  • typescriptからElmを呼び出す
    • 型付きの世界を知ってしまうともうJSには戻りたくない
  • VSCodeで開発しやすくする
    • linterは必須。細かいインデント直すのとか嫌
  • github.ioに自動デプロイ
    • 成果は見れるようにしたい

環境

  • Windows 10
  • Node 14.16.1
  • npm グローバルインストール
    • elm-analyse@0.16.5
    • elm-format@0.8.5
    • elm-test@0.19.1-revision6
    • @elm-tooling/elm-language-server@2.0.3

以下、npm installでpackage.jsonのパッケージをインストールしていることを前提で記す。

package.json
package.json
{
  "name": "eiseiteien",
  "version": "0.0.0",
  "description": "elmのサンプル",
  "main": "index.js",
  "scripts": {
    "dev": "webpack",
    "prod": "webpack",
    "serve": "webpack  serve"
  },
  "repository": {
    "type": "git",
    "url": "git@ssh.dev.azure.com:v3/hibohiboo/%E8%A1%9B%E6%98%9F%E5%BA%AD%E5%9C%92/aws-elm"
  },
  "author": "",
  "license": "MIT",
  "devDependencies": {
    "@elm-tooling/elm-language-server": "^2.1.0",
    "@typescript-eslint/eslint-plugin": "^4.22.1",
    "@typescript-eslint/parser": "^4.22.1",
    "css-loader": "^5.2.4",
    "elm": "^0.19.1-5",
    "elm-format": "^0.8.5",
    "elm-hot-webpack-loader": "^1.1.8",
    "elm-webpack-loader": "^8.0.0",
    "eslint": "^7.25.0",
    "eslint-config-prettier": "^8.3.0",
    "eslint-import-resolver-webpack": "^0.13.0",
    "eslint-plugin-prettier": "^3.4.0",
    "html-webpack-plugin": "^5.3.1",
    "prettier": "^2.2.1",
    "style-loader": "^2.0.0",
    "ts-loader": "^9.1.2",
    "typescript": "^4.2.4",
    "webpack": "^5.36.2",
    "webpack-cli": "^4.7.0",
    "webpack-dev-server": "^4.0.0-beta.3"
  }
}

VSCodeへLinterの導入

  • ソースコード保存時にフォーマットされるようにする
    • editorconfig
    • eslint
    • prettier
    • elmtooling
  • srcフォルダに~のエイリアスを切っている。eslint,tsconfig,webpackの3か所に設定が必要*参考
設定ファイル
.vscode/extensions.json
{
  "recommendations": [
    "editorconfig.editorconfig",
    "dbaeumer.vscode-eslint",
    "esbenp.prettier-vscode",
    "elmtooling.elm-ls-vscode",
  ],
  "unwantedRecommendations": []
}
.vscode/settings.json
{
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true,
  },
  "editor.formatOnSave": true,
  "editor.tabCompletion": "on",
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact"
  ],
  // デフォルトの設定はオフにしておく
  "typescript.format.enable": false,
  "javascript.format.enable": false,
  "liveServer.settings.port": 5502,
  "[scss]": {
    "editor.formatOnSave": false,
    "editor.defaultFormatter": "esbenp.prettier-vscode",
  },
  "css.validate": false,
  "scss.validate": false,
}
.editorconfig
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false
tsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "~/*": ["./src/*"]
    },
    "outDir": "./dist/",
    "sourceMap": true,
    "target": "es6",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": false,
    "forceConsistentCasingInFileNames": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "noImplicitAny": true,
  },
  "exclude": ["node_modules", "__tests__", "cdk"],
  "include": ["next-env.d.ts", "**/*.ts"]
}

.eslintrc.js
const path = require('path')

module.exports = {
  ignorePatterns: ['.eslintrc.js', '.babelrc.js', 'webpack.config.js', 'public'],
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:@typescript-eslint/eslint-recommended',
    'plugin:prettier/recommended',
    'prettier',
  ],
  plugins: ['@typescript-eslint'],
  parser: '@typescript-eslint/parser',
  env: {
    browser: true,
    node: true,
    es6: true,
    jest: true,
  },
  parserOptions: {
    sourceType: 'module',
  },
  rules: {
    'react/prop-types': 'off',
    'react/react-in-jsx-scope': 'off',
    '@typescript-eslint/no-explicit-any': 'off',
    '@typescript-eslint/explicit-function-return-type': 'off',
    '@typescript-eslint/explicit-module-boundary-types': 'off',
    'commma-dangle': 'off',
  },
  settings: {
    'import/resolver': {
      webpack: { config: path.join(__dirname, '/webpack.config.js') },
    },
  },
}

.prettierrc.js
module.exports = {
  semi: false,
  arrowParens: 'always',
  singleQuote: true,
  trailingComma: 'all',
}
elm-tooling.json
{
  "entrypoints": [
    "./src/elm-files/Main.elm"
  ]
}

フォルダ構造

- public
  - template.html
  - main.css
- src
  - elm-files
    - Main.elm
    - Main.elm.d.ts
  - entry
    - index.ts

Webpack

  • ts,elmを1つのjsにバンドル
  • elmはnpm run dev,npm run serveの時はデバッグを有効にする
  • css,htmlはpublicフォルダ内にあるものを利用
設定ファイル
webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

const MODE = ['dev', 'serve'].includes(process.env.npm_lifecycle_event)
  ? 'development'
  : 'production'

const elmRuleBase = {
  test: /\.elm$/,
  exclude: [/elm-stuff/, /node_modules/],
  use: [],
}

const elmRule =
  MODE === 'development'
    ? {
        ...elmRuleBase,
        use: [
          { loader: 'elm-hot-webpack-loader' },
          {
            loader: 'elm-webpack-loader',
            options: {
              debug: true,
            },
          },
        ],
      }
    : {
        ...elmRuleBase,
        use: [
          {
            loader: 'elm-webpack-loader',
            options: {
              optimize: true,
            },
          },
        ],
      }

module.exports = {
  mode: MODE,
  entry: './src/entry/index.ts',
  devtool: 'source-map', // 'inline-source-map',
  module: {
    rules: [
      {
        test: /\.ts$/,
        use: 'ts-loader',
        exclude: /node_modules/,
      },
      {
        test: /\.css/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: { url: false },
          },
        ],
      },
      elmRule,
    ],
  },
  resolve: {
    alias: {
      '~': path.resolve(__dirname, 'src'),
    },
    extensions: ['.ts', '.js'], //  .jsがないと、Can't resolve 'xxxx' が発生する
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: 'webpack Boilerplate',
      template: path.resolve(__dirname, './public/template.html'),
      filename: 'index.html',
    }),
  ],
}
public/template.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>

  <body>
    <div id="root"></div>
  </body>
</html>

Elm Hello

  • フォーマッタが効くかどうか、 main =text "Html hello!" を一行にしてみるなどして試す
    • 効いていない場合、vscodeを再起動してみると直ることが多い
    • それでもダメなら、一度npm run serveするなどしてelmをトランスパイル後、もう一度VSCodeを再起動
src/elm-files/Main.elm
module Main exposing (main)

import Html exposing (Html, text)


main : Html msg
main =
    text "Html hello!"

Typescript EntryPoint

  • ;を入れて保存するなど、eslintが効いていることを確認
  • Elmとhtmlのidを紐づける作業
  • Elmの型は.d.tsの型ファイルを書いてTypescriptに認識させる *参考
src/entry/index.ts
import { Elm } from '~/elm-files/Main.elm'
import '~/../public/main.css'
Elm.Main.init({
  node: window.document.getElementById('root'),
})
src/elm-files/Main.elm.d.ts
export namespace Elm {
  namespace Main {
    interface Args {
      node: HTMLElement
      flags?: Flags
    }

    interface Flags {
      initialValue: string
    }

    function init(args: Args)
  }
}

動作確認

ホットリロードの確認

  • npm run serveで起動
  • http://localhost:8080/ に アクセス
  • Html hello!とテキストが出ていることを確認
  • テキストを書き換えて、ホットリロードされることを確認

image.png

デバッグログの確認

  • ログ出力をソースにいれる
src/elm-files/Main.elm
module Main exposing (main)

import Html exposing (Html, text)


main : Html msg
main =
    let
        _ =
            Debug.log "log" "test"
    in
    text "Html hello!"
  • 開発者ツールのコンソールでログを出力できることを確認

image.png

  • npm run devでトランスパイルできることを確認
  • npm run prod でトランスパイルが失敗することを確認。Elmのエラーメッセージはとても丁寧。

image.png

Github.ioへのデプロイ

Github Actionsを利用する。
actions用のyamlファイルを作成。
deployブランチを切ってpush

Actionファイル
.github/workflows/gh-pages.yml
name: github pages

on:
  push:
    branches:
      - deploy  # Set a branch name to trigger deployment

jobs:
  deploy:
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v2

      - name: Setup Node
        uses: actions/setup-node@v2
        with:
          node-version: '14'

      - name: modules Setup
        run: npm i
        working-directory: .

      - name: trancepile
        run: npm run dev
        working-directory: .


      - name: Move files
        run: |
          mv ./dist ./docs

      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./docs
  • gh-pagesブランチが作成されていることを確認

image.png

  • SettingsからGitHub Pagesの有効化を行う
    • ブランチはgh-pages,フォルダは(/root)を選択

image.png

image.png
image.png

参考

『プログラミングElm』サポートサイト
React: import時のaliasを設定するときはWebpack、TypeScript、ESLintの3つを対応しなければならない件
TypeScript から Elm を import する時にエラーを出さない方法 (Parcel)
GitHub Actionsを使ってGithub PagesにOpen APIのドキュメントを公開したメモ

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?