- 投稿日:2020-04-03T18:51:04+09:00
PHPファイルをhtml-loaderにかけたときにエラーが出るときの対策
PHPプロジェクトをejs(ejs-html-loader)で管理していて、共通ヘッダーのincludeなど、
?>
で終わらないファイルが存在する場合、productionビルドでエラーが出る。Parse Error: <?phpこれは html-loader(正確にはそこから利用される html-minifier-terser )のminimizeオプションでパースエラーとなるため。
これを回避するためには html-loader に
continueOnParseError: true
を指定してやれば良い。webpack.config.jsconst ExtractTextPlugin = require('extract-text-webpack-plugin'); module.exports = [ { context: path.resolve(__dirname, 'dev'), entry: { // ?> で終わらないファイルを含む index: './index.php'. header: './header.php' }, output: { path: path.resolve(__dirname, 'public'), filename: '[name].php' }, module: { rules: [ { test: /\.php/, exclude: /node_modules/, use: ExtractTextPlugin.extract({ fallback: "raw-loader", use: [ { loader: 'html-loader', options: { minimize: { continueOnParseError: true } } }, { loader: 'ejs-html-loader', options: { context: { // タイムスタンプをPHPファイルに渡すとか timestamp: +new Date() } } } ] }) } ] }, plugins: [ new ExtractTextPlugin('[name].php') ] } ];
- 投稿日:2020-04-03T15:59:08+09:00
Nodeでmultipart/form-dataを送る
form-data
を使う。Axios
const axios = require("axios"); const FormData = require("form-data"); const form = new FormData(); form.append("message", "hello"); const res = await axios.post(API, form, { headers: form.getHeaders() });node-fetch
const fetch = require("node-fetch"); const FormData = require("form-data"); const form = new FormData(); form.append("message", "helllo"); const res = await fetch(API, { method: "POST", body: form });なぜ
node-fetch
はヘッダーでgetHeaders()
を利用しなくていいのか?理由は単純で、
form-data
を特別に扱っている。
https://github.com/node-fetch/node-fetch/blob/c167190c6ee21234ba41bd8430700d4720bf64ce/src/body.js#L320-L323// Detect form data input from form-data module if (body && typeof body.getBoundary === 'function') { return `multipart/form-data;boundary=${body.getBoundary()}`; }さて、
multipart/form-data;
はいいにしても、boundary
とはなんだろうか。
boundary
とは、複数の情報を続けて送る際、データの仕切り線の役割を果たす。
form-data
の実装を見ると、-
が26文字、ランダムな数値が24字の50文字の仕切り線を生成する。FormData.prototype._generateBoundary = function() { // This generates a 50 character boundary similar to those used by Firefox. // They are optimized for boyer-moore parsing. var boundary = '--------------------------'; for (var i = 0; i < 24; i++) { boundary += Math.floor(Math.random() * 10).toString(16); } this._boundary = boundary; };これに
\r\n
を付加したものを区切りにする。
詳しくは、以下の記事に詳しい。
- 投稿日:2020-04-03T13:15:16+09:00
Node.js Worker Threads: TypeScriptのワーカーを起動する方法 〜ts-node、ts-node-devに対応する方法〜
Node.jsのWorker Threadsは、本物のスレッドプログラミングができます。ワーカーの処理を記述したJavaScriptを与えて、ワーカーを起動するわけですが、TypeScriptのファイルを指定するにはどしたらいいのでしょうか?
本稿でわかること
- ts-nodeとWorker Threadsを組み合わせて、TypeScriptのワーカーを起動する方法
- ts-node-devでTypeScriptのワーカーを起動する方法
前提知識
本稿を理解するにあたっては、下記の技術についての基礎的な知識が必要です。
- Worker Threads
- 本物のスレッドプログラミングができるNodeモジュール。
- 概要と基礎的な使い方は、次の投稿をご覧ください。
- ts-node
- TypeScriptのコンパイルとJavaScriptの実行をコマンド一つでできるツール。
tsc && node dist/main.js
を一発でできるようにしたツール。- nodeコマンドの感覚でTypeScriptを実行できる。 例:
ts-node src/main.ts
- ts-node-dev
- ts-nodeとnode-devを組み合わせた開発ツール。
- TypeScriptのコードに変更が加わると自動的にコンパイルし、プログラムを再起動してくれる。
解決したい課題: WorkerにTypeScriptを指定することはできない
JavaScriptでWorker Threadsを実行する方法は以下のような手順になります。
まず、ワーカー側の処理を実装したJavaScriptファイルを作ります:
worker.jsconsole.log('Hello from worker')次に、ワーカーを起動する処理を書くのですが、
Worker
コンストラクタでワーカーのファイル名を指定する必要があります:main.jsconst {Worker} = require('worker_threads') const worker = new Worker('./worker.js')このmain.jsを実行すると、ワーカーが起動することが確認できます:
$ node main.js Hello from workerこのコードをTypeScriptで書き直し、ts-nodeで同じように実行するとどうなるでしょうか? やってみましょう。
まず、ワーカー側の実装をTSに移植します。内容はworker.jsと全く同じです:
worker.tsconsole.log('Hello from worker')次に、main.jsをTSに移植します。大きな変更は、
worker.js
ではなくworker.ts
を起動するように変える点です:main.tsimport {Worker} from 'worker_threads' new Worker(__dirname + '/worker.ts') // tsファイルを指定このコードを、ts-nodeで起動してみます。すると、次のようなエラーが発生し、ワーカーが起動できないことが分かります。エラー内容は、「ワーカースクリプトの拡張子はjs, mjs, cjsじゃないとダメだよ」というものです。
$ ts-node src/main.ts The worker script extension must be ".js", ".mjs", or ".cjs". Received ".ts"このことから、Worker Threadsでは直接TypeScriptファイルが指定できないことが分かったと思います。
解決策: いったんJavaScriptファイルを経由するようにする
Worker Threadsで起動できるコードはJavaScriptのみという制約があるので、直接TypeScriptのワーカーを起動するのはあきらめます。迂回手段として、まずJavaScriptのワーカーを起動し、その中でTypeScriptコードを
require
するようにします。先述した失敗作TypeScriptコードを手直ししていきましょう。
まず、main.tsはworker.tsではなく、worker.jsを起動するように直します:
main.tsimport {Worker} from 'worker_threads' new Worker(__dirname + '/worker.js') // jsを起動するように直す次に、最終的な目的地である、TypeScriptワーカーのファイルを作ります。名前はworker.jsと区別できるようtsWorker.tsにしておきます:
tsWorker.tsconsole.log('tsWorker.ts started')最後に、main.tsとtsWorker.tsを橋渡しする、worker.jsを実装します。worker.jsの重要な役割は、ts-nodeをregisterすることです。これにより、以降のコードではtsファイルをrequireして実行できるようになります:
worker.jsconsole.log('worker.js started') require('ts-node').register() // 重要 require(__dirname + '/tsWorker.ts')このコードを実行してみましょう。
$ ts-node src/main.ts worker.js started tsWorker.ts started出力結果から、まずworker.jsが実行され、次にtsWoekr.tsが読み込まれ実行されたことがわかると思います。
ts-node-devでは、execArgvを空っぽにしてWorkerを起動する
これまでts-nodeでTypeScriptワーカーを起動する方法を説明してきましたが、類似のツールであるts-node-devでも同じ方法で対応できるのでしょうか? 結論を言うと、そのままでは対応できません。
上のmain.tsをts-node-devで実行してみると分かりますが、worker.jsは起動するものの、worker.js内のrequireが動作せず、スレッドが終了してしまいます:
$ ts-node-dev src/main.ts Using ts-node version 8.8.1, typescript version 3.8.3 worker.js startedworker.jsで
execArgv
を確認すると、ワーカー側では不要なts-node-devのフックが渡ってきているのがわかります:worker.jsconsole.log('worker.js started') console.log(process.execArgv) // require('ts-node').register() // require(__dirname + '/tsWorker.ts')実行結果$ ts-node-dev src/main.ts Using ts-node version 8.8.1, typescript version 3.8.3 worker.js started [ '-r', '/var/folders/4l/mrmcxh3x40lbcpwxyz29ppcw0000gn/T/ts-node-dev-hook-1629690677650566.js' ]解決策としては、main.tsの
new Worker
のオプションでexecArgv
をカラにすることです:main.tsimport {Worker} from 'worker_threads' new Worker(__dirname + '/worker.js', {execArgv: []})こうしておくと、まずはts-node-devでもTypeScriptのワーカーが起動できるようになります。
$ ts-node-dev src/main.ts Using ts-node version 8.8.1, typescript version 3.8.3 worker.js started tsWorker.ts startedしかし、この方法に問題がないわけではありません。ts-node-devの醍醐味としては、TypeScriptのコードを書き直したら、自動的に再コンパイルして、プロセスを起動しなおしてくれることです。しかし、この対処法では、スレッド側でrequireされたファイルをいくら修正しても、自動再コンパイル&再起動はされません。
この課題の解決策については、また時間を見つけて調べてみたいと思います。
- 投稿日:2020-04-03T12:52:05+09:00
重複しない任意長のランダム文字列を生成する方法
TL;DR
高速で安全、手軽にランダム文字列を生成したいときはai/nanoidを使う。
特徴
README.mdより意訳しています。
- 手軽:サイズ108bytes (min & gzip)、依存パッケージ無し
- 高速:UUIDより40%速い
- 安全:重複しづらく、予測されにくく、偏りが小さい
- 短い:
[a-zA-Z0-9_-]
を使うので生成される文字列を安全に短くできる- 多言語:
Node.js
以外にも14のプログラミング言語で利用できる使い方
デフォルトでは
[a-zA-Z0-9_-]
から21文字で生成されます。import { nanoid } from 'nanoid' nanoid() //=> "yVQk_rn0A60LXcOR-2voE"非同期に生成する
import { nanoid } from 'nanoid/async' async function createUser () { user.id = await nanoid() }任意の長さにする
短くするほど重複しやすくなるので注意してください。
重複しやすさを簡易計算できるサイトもあるので参考にしましょう。nanoid(10) //=> "3juqViJh32" nanoid(3) //=> "eEh"出現する文字を絞る
customAlphabet
の引数に使いたい文字と文字長を指定すると、ランダム文字列生成関数が作れます。import { customAlphabet } from 'nanoid' const randomColor = customAlphabet('1234567890abcdef', 6) font.color = `#${randomColor()}` //=> "#a1ea8e"ちなみに、CyberAP/nanoid-dictionaryというパッケージを利用すれば、使いたい文字に指定できる頻出パターンが入っています。
numbers
:0〜9の数字lowercase
:小文字の英字uppercase
:大文字の英字nolookalikes
:a-zA-Z0-9
から見間違いやすい1
,l
,I
,0
,O
,o
,u
,v
,5
,S
,s
を取り除いたもの記事執筆時点のバージョン
// $ cat node_modules/nanoid/package.json | jq '{ name, version }' { "name": "nanoid", "version": "3.0.2" }
- 投稿日:2020-04-03T02:00:11+09:00
[WIP] part2 Express+TypeScriptを使い始める | Node.jsで在庫管理アプリを作ってみる
前 次 part1 part3 注意
この記事は進捗報告のようなものです。アプリが完成するかは未定です。
記事は見栄えがよくなるように書いていますが、実際の作業には手戻りもあったりしたので、コミット履歴とちょっと食い違ったりします。設計の続き
前回設計した仕様を実現するために、どういう構成にすればいいか考えてみました。
- プラットフォーム: Node.js
- フロントエンド: React, Bootstrap
- バックエンド: Express, MySQL
プラットフォームはJavaScriptの経験が活かせるNode.jsにしました。実はJavaScriptの経験があると言ってもNode.jsで開発するのはこれが初めてです。お手柔らかにお願いします。
フロントエンドはReactとBootstrapにしました。フレームワークをReactとVueで迷いましたが、シェアが多いReactに決めました。潰しが効きそうなので。レイアウトは楽な方がいいのでBootstrapを付けます。
バックエンドにサーバとしてExpress、データベースとしてMySQLを採用します。MySQLは授業で使ったので、Expressはちょっとググって良さそうだったので決めました。開発開始
それでは開発をはじめます。とりあえず、サーバを立てるところから。
目標 サーバが動くこと ExpressとTypeScriptをインストール
https://expressjs.com/ja/starter/hello-world.html
https://qiita.com/pochopocho13/items/79a4735031ce11a91df7
上記を参考にしました。# リポジトリを作成 $ mkdir zaiko $ cd zaiko $ git init $ git remote add $ npm init # entrypointはapp.jsに指定 # Expressをインストール $ npm i --save express # TypeScriptをインストール $ npm i --save-dev typescript @types/node ts-nodeここで、
$ npm run tsc
でコンパイルできるように、package.jsonに以下を追加しておきます。package.json"scripts": { "tsc": "tsc" }# TypeScript初期化 $ npm run tsc -- --init $ npm run tsc -- -v Version 3.8.3helloworldプログラムを書く
ようやくプログラムまでたどり着きました。富士山で言うなら山梨県の県境です。
app.tsimport express from 'express' const app: express.Express = express() app.get("/", (req, res) => res.send("Hello, world!")) app.listen(3000, () => console.log("Example app listening on port 3000!"))TypeScriptはいつかやってみたいと思っていまいた。ようやくできました。
初ビルド&実行
では実行してみます。
$ npm run tsc $ node app.js Example app listening on port 3000!http://localhost:3000/ にアクセスしてみると、
Hello, world!
と表示されました。
- 投稿日:2020-04-03T00:58:27+09:00
[WIP] part1 設計 | Node.jsで在庫管理アプリを作ってみる
次 part2 注意
この記事は進捗報告のようなものです。アプリが完成するかは未定です。
在庫管理アプリがほしい
私は物持ちです。我が家にはたくさんの食材や調味料などが貯蓄してあります。その量と言ったら私自身も把握が難しい程です。レトルトカレーでいうと、縦にして並べても0.7平方メートルくらいあります。平らに並べると多分卓球台の面積くらいあるんじゃないですかね。卓球が好きなわけじゃないですけど。
貯蓄が多いと言ったらプラスに聞こえますが、把握できていないので偏りが出たりします。例えば未開封のチューブのワサビは2本あるのにチューブのショウガが足りないということもあります。先に言ったレトルトカレーは、賞味期限が2016年から2022年のまであります。消費と追加のバランスが平衡していないと腐ってしまうのですが、何が余っているか、何が足りないかが把握できていないので買い物がうまくいきません。
買い物するときに、家に何があるか確認できるといいな、ということで在庫管理のシステムを考えました。店でスマホを開き、調味料を買おうと思ったときに「調味料」の欄を見て、何がどれだけあるかを把握できるといいです。さらに言うなら、今足りていないものもひと目でわかるといいです。どんな感じにしよう
機能
- 在庫を追加する機能。買ったときとかに。
- 在庫を減らす機能。使い終えたときとかに。
- 在庫を確認する機能。買い物のときに必要です。
機能は以上でいいでしょう。
データ
- 分類を作ってそこに放り込めるといいと思います。粉ものとか、調味料とか、お惣菜とか。
- 賞味期限や消費期限もわかるといいです。期限切れの食品や期限間近は腐る前に消費すべきですから。
- 保存場所もあるといいです。冷蔵庫か冷凍庫か引き出しか知っておくと探す手間が省けます。
よし、テーブルが決まりました。レコードは
(品名, 分類, 場所, 期限, 個数)
です。主キーは(品名, 場所, 期限)
ですかね。分類→品名
の推移従属があるのでテーブルを分割して在庫(品名, 場所, 期限, 個数)
と品物(品名, 分類)
にしましょう。いちいち「ボンカレー、レトルト食品」みたいな分類を入力するのは面倒ですし、品名を書くと勝手に埋めてくれるといいですね。非機能案件です。テーブルはこんな感じです
在庫
品名 場所 期限 個数 ボンカレー 引き出し 2016/03/28 5 ボンカレー 引き出し 2017/10/01 10 ボンカレー 引き出し 2022/04/01 10 わさびチューブ 冷蔵庫 2020/02/22 1 わさびチューブ 引き出し 2020/02/22 2 品物
品名 分類 ボンカレー レトルト食品 わさびチューブ 調味料 おわり
では、次回から早速作り始めたいと思います。