20200403のNode.jsに関する記事は6件です。

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.js
const 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')
        ]
    }
];

参考: 【備忘録】HTMLMinifierの全オプションについて調査した

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Nodeでmultipart/form-dataを送る

form-data を使う。

form-data/form-data: A module to create readable "multipart/form-data" streams. Can be used to submit forms and file uploads to other web applications.

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文字の仕切り線を生成する。

https://github.com/form-data/form-data/blob/d7026253e728af9568503dc3dc55cd1a566605e0/lib/form_data.js#L342-L351

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を付加したものを区切りにする。
詳しくは、以下の記事に詳しい。

いまさら聞けないHTTPマルチパートフォームデータ送信 - SATOXのシテオク日記

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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
  • 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.js
console.log('Hello from worker')

次に、ワーカーを起動する処理を書くのですが、Workerコンストラクタでワーカーのファイル名を指定する必要があります:

main.js
const {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.ts
console.log('Hello from worker')

次に、main.jsをTSに移植します。大きな変更は、worker.jsではなくworker.tsを起動するように変える点です:

main.ts
import {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.ts
import {Worker} from 'worker_threads'

new Worker(__dirname + '/worker.js') // jsを起動するように直す

次に、最終的な目的地である、TypeScriptワーカーのファイルを作ります。名前はworker.jsと区別できるようtsWorker.tsにしておきます:

tsWorker.ts
console.log('tsWorker.ts started')

最後に、main.tsとtsWorker.tsを橋渡しする、worker.jsを実装します。worker.jsの重要な役割は、ts-nodeをregisterすることです。これにより、以降のコードではtsファイルをrequireして実行できるようになります:

worker.js
console.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 started

worker.jsでexecArgvを確認すると、ワーカー側では不要なts-node-devのフックが渡ってきているのがわかります:

worker.js
console.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.ts
import {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されたファイルをいくら修正しても、自動再コンパイル&再起動はされません。

この課題の解決策については、また時間を見つけて調べてみたいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

重複しない任意長のランダム文字列を生成する方法

TL;DR

高速で安全、手軽にランダム文字列を生成したいときはai/nanoidを使う。

特徴

README.mdより意訳しています。

使い方

デフォルトでは[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:大文字の英字
  • nolookalikesa-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"
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[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.3

helloworldプログラムを書く

ようやくプログラムまでたどり着きました。富士山で言うなら山梨県の県境です。

app.ts
import 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!と表示されました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[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

品物

品名 分類
ボンカレー レトルト食品
わさびチューブ 調味料

おわり

では、次回から早速作り始めたいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む