2020/11版:TypeScript+UmbrellaJS+PostCSSの開発環境を yarn で構築する
今回は開発環境ということで minify や uglify などは行わないところまでとします。
また、 git や npm のローカルの準備は既に終わってるものとします。
TL;DR
- 基本的には昨日までの記事のまとめです
- 2020/11版:TailwindCSS + PostCSS の開発環境を yarn で構築する
- 2020/11版:TypeScript の開発環境を yarn で構築する
- あとはリポジトリみてね
yarn を初期化して必要なパッケージを入れる
yarn init -y
mkdir public
mkdir -p src/ts
mkdir src/css
yarn add tailwindcss postcss postcss-cli postcss-import postcss-nested --dev
yarn add typescript sirv-cli ttypescript @zoltu/typescript-transformer-append-js-extension rimraf yarn-run-all --dev
typescript / ttypescript 初期設定
yarn run tsc --init
tsconfing.json 編集
{
"compilerOptions": {
"target": "ES2017",
"module": "es2015",
"sourceMap": true,
"outDir": "./public/js",
"rootDir": "src/ts",
"removeComments": true,
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"plugins": [
{
"transform": "@zoltu/typescript-transformer-append-js-extension/output/index.js",
"after": true,
}
]
}
}
TailwindCSS / PostCSS 初期設定
yarn run tailwindcss init -p
tailwind.config.js
module.exports = {
future: {
// removeDeprecatedGapUtilities: true,
// purgeLayersByDefault: true,
},
purge: [],
theme: {
extend: {},
},
variants: {},
plugins: [],
}
postcss.config.js
module.exports = (ctx) => ({
map: ctx.options.map,
plugins: [
require('postcss-import'),
require('tailwindcss'),
require('postcss-nested'),
require('autoprefixer'),
],
})
テスト用のファイルを用意する
ts, css は冒頭に書いた前回までの記事を参照してもらうとして、
index.html はこんなのを用意しておきます
<!DOCTYPE html>
<html>
<head>
<title>index</title>
<link href="./css/global.css" rel="stylesheet" type="text/css">
<link href="./css/tailwindcss.css" rel="stylesheet" type="text/css">
<script type="module" src="./js/index.js"></script>
</head>
<body>
<h1>FooBar</h1>
<p id="foo"></p>
<p class="text-purple-600"><strong>strong</strong><p>
<div class="btn">btn</div>
</body>
</html>
出力テスト
yarn postcss src/**/*.css -d public/css
yarn ttsc
yarn sirv public --port 8080
出力結果
うん!良い感じ!!
Umbrella JS を導入する
今回はDOM操作ライブラリとして Umbrella JS を導入します。
Umbrella JS はjQueryライクなDOM操作、イベント、Ajaxサポート中心の軽量ライブラリですね。
yarn add umbrellajs --dev
- Umbrella JS
- Tiny library for DOM manipulation and events
- https://umbrellajs.com/
- umbrellajs | yarnpkg
- Documentation | Umbrella JS
- Umbrella JSドキュメント翻訳 | Umbrella - にほんご。
- 最終更新2018年でちょっと古いけどにほんご
- https://tr.you84815.space/umbrella/index.html
Umbrella JS、私はわりと好きなのですが TypeScript で開発するにはしんどい問題があります。
それは、 DefinitelyTyped で @types/umbrellajs
がまだ提供されていないのです。
- DefinitelyTyped / DefinitelyTyped | GitHub
なので自分でどうにかするしかないですね。💦
- TypeScript未対応のモジュールをimportするときのエラー対策 | Qiita
- TypeScript で型定義ファイル( d.ts )がないときの対処法 | Qiita
- TypeScriptでJavaScriptのライブラリを使用するには? | RE:ENGINES
- npm module に typescript の型定義がない時に、とりあえずビルドが通るようにする | by ryutamaki | Medium
今回取ろうとした手段1:無視してanyのままやる
//@ts-ignore
import u from "umbrella";
はい。OKです。トランスパイルが通ります。
今回取った手段2:自分で型定義ファイルを用意する
今回はこちらを選択しました。
1でもよかったのですがコード補完が全く効かないのは「なんで私はTypeScriptを書いているんだっけ?」という気持ちになるのでこちらを選択することにしました。
1. @Types 配下に umbrella.d.ts を用意する
./src/ts/@types/umbrella.d.ts
(./src/@types/umbrella.d.ts でもOK)
/* ======================= */
/* 全然足りてません!未完成です!! */
/* ======================= */
declare module "umbrella" {
interface umbrellaElement<T extends HTMLElement> extends T {
addClass(val: string): void;
length: number;
}
interface umbrellaElementList<T extends HTMLCollection> extends T {
nodes: Array;
}
function u<T extends HTMLElement>(val: string):umbrellaElement<T>;
function u<T extends HTMLCollection>(val: string):umbrellaElementList<T>;
export default u;
}
interface に Generics 適用するところで詰まったけどここに助けられた
- Suggestion: Interface extend generic type #2225 | microsoft / TypeScript | GitHub ISSUE
この型定義ファイルを使うときに Input タグとかを使う時は
u<HTMLInputElement>
とするなどしてください
- TypeScriptで document.getElementById("hoge").value をすると出るThe property ‘hoge' does not exist on value of type 'HTMLElement' というエラーを解消する | Qiita
dtsmake
や DefinitelyTyped でも推奨されている dts-gen
も試してみたのですが
満足のいく結果が得られなかったので今回は自作することにしました。
ある程度定義が揃ったら DefinitelyTyped に出して貢献したいですね……。
- TypeScript型定義ファイルのコツと生成ツール dtsmake | Qiita
- microsoft / dts-gen | GitHub
2. import すればOK
src/ts/index.ts
import { addBaz } from "./baz"
import u from "umbrella";
window.onload = function () {
const app = document.getElementById("foo") as HTMLParagraphElement;
app.innerHTML ="Hello Typescript World! foo";
addBaz(app);
u<HTMLParagraphElement>("#foo").addClass('main');
console.log("#foo nodes:", u<HTMLParagraphElement>("#foo").length);
};
3. トランスパイル後のJavaScriptでも読み込めるようにする
TypeScript は import の解決まではやってくれないので
import u from "umbrella";
をトランスパイル後にも参照できるように
自分でどうにかしてやる必要があります。
また、 @zoltu/typescript-transformer-append-js-extension
は相対パスは解決してくれるものの node_modules から参照しているものは処理の対象外にしているようです。
なので、ここはまた ttypescript
の力を借りて別のプラグインを入れましょう。
yarn add ts-transform-import-path-rewrite --dev
yarn add
したら例のごとく tsconfig.json
に追記します。
"transform": "ts-transform-import-path-rewrite"
のブロックを追加、
umbrella
を相対パスでの ./umbrella.js
の参照に置換します。
"plugins": [
{
"transform": "ts-transform-import-path-rewrite",
"import": "transform",
"alias": {
"^(umbrella)$": "./$1.js"
},
"after": true,
"afterDeclarations": true,
"type": "config"
},
{
"transform": "@zoltu/typescript-transformer-append-js-extension/output/index.js",
"after": true,
}
]
これで yarn ttsc
でトランスパイルしてやると、こうなります。OKですね。
src/ts/index.ts
import u from "umbrella";
↓
public/js/index.js
import u from "./umbrella.js";
- ts-transform-import-path-rewrite | yarnpkg
4. node_modules 配下のファイルを public/js 配下にコピーする
参照が出力できたのであとは実体を用意してやるようにします。
public/js に パッケージに含まれている umbrella.esm.js
を umbrella.js
にリネームしてコピーするようにしましょう。
yarn add cpy-cli --dev
# 試してみる
yarn cpy node_modules/umbrellajs/umbrella.esm.js public/js/ --rename=umbrella.js
これで public/js/umbrella.js も用意できるようになりました! OKですね!
- cpy-cli | yarnpkg
今回はわざわざ cpy-cli
を使いましたがコレでもいけそうですね
node -e "require('fs').copyFileSync('./node_modules/umbrellajs/umbrella.esm.js', './public/js/umbrella.js')"
- javascript - Rename file with NPM - Stack Overflow
さすがに、ここまでやりだすと webpack とか使った方が早いなという気持ちになってきます😂
yarn run の scripts を書く
今回は色々ありますが全部順次実行するようにしましょう。
"scripts": {
"cleanjs": "rimraf ./public/js/",
"cleancss": "rimraf ./public/css/",
"clean": "run-s cleanjs cleancss copyumbrella",
"postcss": "postcss src/**/*.css -d ./public/css",
"watchpostcss": "postcss src/**/*.css -d ./public/css --watch",
"copyumbrella": "cpy node_modules/umbrellajs/umbrella.esm.js public/js/ --rename=umbrella.js",
"ttsc": "ttsc",
"watchts": "ttsc --watch",
"cleanwatch": "run-p clean watchpostcss watchts",
"serve": "sirv public --port 8080",
"build": "run-s cleanjs cleancss copyumbrella postcss ttsc",
"dev": "run-p cleanwatch serve"
},
読めばわかりますが簡単に解説を入れておくと
-
cleanjs
rimraf ./public/js/
-
./public/js/
ディレクトリを削除
-
cleancss
rimraf ./public/css/
-
./public/css/
ディレクトリを削除
-
clean
run-s cleanjs cleancss copyumbrella
- ディレクトリをお掃除(削除)して
public/js/umbrella.js
をコピーして作成
-
postcss
postcss src/**/*.css -d ./public/css
- postcss でトランスパイル
-
watchpostcss
postcss src/**/*.css -d ./public/css --watch
- postcss でトランスパイルして watch モード
-
copyumbrella
cpy node_modules/umbrellajs/umbrella.esm.js public/js/ --rename=umbrella.js
-
public/js/umbrella.js
をコピーして作成
-
ttsc
ttsc
- TypeScript を
ttypescript
でトランスパイル
-
watchts
ttsc --watch
- TypeScript を
ttypescript
でトランスパイルして watch モード
-
cleanwatch
run-p clean watchpostcss watchts
- cleanしてwatchモードを並列で起動(cleanの完了をまってないのでよくない気がする)
-
serve
sirv public --port 8080
- 静的ファイル配信サーバー起動
-
build
run-s cleanjs cleancss copyumbrella postcss ttsc
- お掃除してcssとtsをトランスパイル
-
dev
run-p cleanwatch serve
- お掃除してcssとtsをトランスパイルしてwatchモードとサーバ起動
最終的な package.json はこんな感じです。
{
"name": "my_project",
"version": "1.0.0",
"main": "index.js",
"repository": "git@github.com:<userid>/my_project.git",
"author": "your name",
"license": "MIT",
"scripts": {
"cleanjs": "rimraf ./public/js/",
"cleancss": "rimraf ./public/css/",
"clean": "run-s cleanjs cleancss copyumbrella",
"postcss": "postcss src/**/*.css -d ./public/css",
"watchpostcss": "postcss src/**/*.css -d ./public/css --watch",
"copyumbrella": "cpy node_modules/umbrellajs/umbrella.esm.js public/js/ --rename=umbrella.js",
"ttsc": "ttsc",
"watchts": "ttsc --watch",
"cleanwatch": "run-p clean watchpostcss watchts",
"serve": "sirv public --port 8080",
"build": "run-s cleanjs cleancss copyumbrella postcss ttsc",
"dev": "run-p cleanwatch serve"
},
"devDependencies": {
"@zoltu/typescript-transformer-append-js-extension": "^1.0.1",
"cpy-cli": "^3.1.1",
"postcss": "^8.1.7",
"postcss-cli": "^8.2.0",
"postcss-import": "^13.0.0",
"postcss-nested": "^5.0.1",
"rimraf": "^3.0.2",
"sirv-cli": "^1.0.8",
"tailwindcss": "^1.9.6",
"ts-transform-import-path-rewrite": "^0.3.0",
"ttypescript": "^1.5.12",
"typescript": "^4.0.5",
"umbrellajs": "^3.2.3",
"yarn-run-all": "^3.1.1"
}
}
コマンドはこんな感じです。
```bash
# お掃除してトランスパイルしてファイルを揃える
yarn build
# お掃除してcssとtsをトランスパイルしてwatchモードとサーバ起動
# Ctrl+c で停止
# TypeScript ファイル/cssファイルを編集して保存すればトランスパイルが走る
# ブラウザで確認しているときは保存後、ハード再読み込みしましょう
yarn dev
以上です。おつかれさまでした。
今回のプロジェクトファイル一式はこちらです。
なんで gulp/webpack 使わないの?
yarn でイケたからです。でも今回はちょっとしんどかった……😂
なんで babel 入れてないの?
IE11対応の予定はないし 前述のES2015対応状況などをみるに私の要件には不要と判断したからです
なんでこんな構成にしたの?
今作ってるサービスのプロトタイプが Ruby on Rails で ERBテンプレートエンジンを使うんですが
フロント側をちょっとDOMいじったり TailwindCSS 使ったりするのに
こういう構成にしました。
なので普段の開発のときは yarn build
しか使わないです。
(ファイル配信はRailsがやってくれるので。)
そのうち Rails と一緒にしたものも記事にします。
なんで Blitz.js とか使わないの?
私もそう思います。
その他の参考サイト
Unbrella JS の example によく出てくる Fetch API 関連
- Fetch API - Web API | MDN
- JavaScriptのFetch API について | Qiita
- まだXMLHttpRequestを使ってるの? fetchのすすめ | Qiita
- jQuery.ajax()の代替としてFetch APIをざっくり使ってみる | tacamy--blog
Discussion