- 投稿日:
新しくセットアップしたTypeScriptのプロジェクトでauto-importが機能しなかったので原因を調べた。
ここ数年ちまちま起きて、そろそろイラついて我慢の限界を覚えたので…。
確認環境
Env | Ver |
---|---|
VSCode | 1.83.0 |
typescript | 4.8.4 |
再現方法
再現用のサンプルリポジトリでsrc/index.ts
の下図コードに対してauto-importが発動する操作を行う。
発生条件
恐らく以下を全て満たすときにauto-import操作をしようとした時に発生する。地味にややこしい。
node_modules
配下に存在し、@types/
モジュールを持たないものでかつ、同一プロジェクト内でimportされておらず、package.json
のdependencies
に書かれていない。
因みにhoge/piyo/fuga
のようなモジュールはhoge
だけがdependencies
にいてもauto-importが動かない。これを全部package.json
のdependencies
に書いていくのは目眩がするので正直auto-importを諦めて自分でimportを調べて書くのが無難だと思う。
発生原因
TypeScript 4.0で実装されたSmarter Auto-Importsのせい。
node_modules
をクロールすると重すぎるので@types/
だけ読み込んで、あとはpackage.json
のdependencies
も見るようにしたよという内容らしい。
解消方法
package.json
のdependencies
に全部のモジュールを書いていくというのが解決方法になるが、気が遠くなるので諦めた方がいい。初回だけは苦痛でもimportパスをどうにかして調べて手動で書いて、あとはauto-importされるのを期待するのが良いだろう(そんな何度もimportしないと思うが…)
取り敢えず基本npm i
で入れて行き、スラッシュ区切りのやつは諦めるくらいがちょうどいいだろう。
再現用のサンプルリポジトリではpackage.json
のdependencies
に"firebase/app": "^10.4.0"
を追加することでsrc/index.ts
のinitializeApp
に対しauto-importが発動する様になるはずだ。
あとがき
早い話、node_modules
配下にあって@types/
を持たないものはTypeScriptに対応する気がないんだな程度に思っておくのが良いだろうが、TS化で@types/
を廃止したライブラリがある当たり、すごく微妙な感じがある。ちょっとなんとかしてほしい。
そもそもpackage.json
のdependencies
はnpm packageを公開する時に動作するために必要な依存関係を登録する場所で、型の補助をする場所ではなかったはずだ。
少なくともnpmjsのdevDependenciesには以下の記述がある。つまりモジュールを配布する時に動作に不要な依存関係を含まないようにするためにdevDependencies
があるということだ。つまりdependencies
には動作に必要なものだけを入れるべきで、モジュールを配布しない場合、このフィールドは不要になるはずである。
If someone is planning on downloading and using your module in their program, then they probably don't want or need to download and build the external test or documentation framework that you use.
In this case, it's best to map these additional items in a devDependencies object.
ただTypeScriptがdependencies
に書かないとauto-importが失敗すると言っているので使うものはdependencies
に入れるというのが良いのだろう。(配布しないモジュールだとしても)
ただそれにしてもauto-importを動かすためだけに、dependencies
に同一モジュールのスラッシュ違いを大量に入れていくのはバカバカしいと思う。
"dependencies": {
"firebase": "^10.4.0",
"firebase/app": "^10.4.0",
"firebase/database": "^10.4.0",
"firebase/analytics": "^10.4.0",
...
}
かといってinitializeApp
がfirebase/app
にあるなんて知らんわけで、全部のパスに総当りしていくのも嫌だし、一々リファレンス漁るのも面倒だし、なんかもうちょっといい具合になって欲しい…。
- 投稿日:
確認環境
Env | Ver |
---|---|
OS | Ubuntu 20.04.4 LTS |
PHP | 8.0.29 |
nginx | 1.18.0 |
手順
php-fpmの導入
sudo apt install php8.0-fpm
sudo sed -i -e 's/;listen.mode = 0660/listen.mode = 0666/' /etc/php/8.0/fpm/pool.d/www.conf
sudo service php8.0-fpm start
nginxの設定
設定ファイルを開きPHPを動かす設定を書く
location ~ ^/.*$ {
root /path/to/www;
fastcgi_pass unix:/run/php/php8.0-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
- 投稿日:
undefinedの判定方法が複数あるということでundefined判定の処理速度比較をしてみたのでその結果。
端的に言うと、hoge === undefined
とtypeof hoge === 'undefined'
の二方式がある。後者は原則考慮不要だが、言語仕様上存在しているので比較したが、現実的に見た場合、どちらで記述した場合でも処理速度に有意な差はないように感じた。
確認環境
Env | Ver |
---|---|
Node.js | 20.1.0 |
TypeScript | 4.9.5 |
@swc/core | 1.3.8 |
比較結果
hoge === undefined
の方が早く見えるが実行するタイミングで変わるので誤差の範疇だと思う。
方式 | ms |
---|---|
hoge === undefined |
4,514 |
typeof hoge === 'undefined' |
4,515 |
確認コード
const tyof = (param?: string) => {
return typeof param === 'undefined';
};
const undef = (param?: string) => {
return param === undefined;
};
const tyStart = +new Date();
for (let i = 0; i < 10000000000; i++) {
tyof();
}
console.log('typeof', +new Date() - tyStart);
const unStart = +new Date();
for (let i = 0; i < 10000000000; i++) {
undef();
}
console.log('undefined', +new Date() - unStart);
TSから生成されたJS
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
const tyof = (param)=>{
return typeof param === 'undefined';
};
const undef = (param)=>{
return param === undefined;
};
const tyStart = +new Date();
for(let i = 0; i < 10000000000; i++){
tyof();
}
console.log('typeof', +new Date() - tyStart);
const unStart = +new Date();
for(let i = 0; i < 10000000000; i++){
undef();
}
console.log('undefined', +new Date() - unStart);
あとがき
MDNを読む限りtypeof hoge === 'undefined'
は該当変数が存在しない場合に有用なようであるが、TypeScriptで書いている場合、通常このようなコードが生まれることがなく、仮に起きるとした場合、次のようなコードになるため現実的に考慮する必要はない。なおMDNにも「こんなことはしないこと」と書いてあるので、一般的なコードでないことは客観的にも伺えるだろう。
(() => {
const undefined = 123;
const hoge = undefined;
if (typeof hoge === 'undefined') {
console.log('hoge is undefined');
} else {
console.log('hoge is not undefined');
}
})();
上記コードの実行結果としてはhoge is not undefined
が出力される。
このコードの主な問題点
const undefined = 123;
というコードは予約語を変数名にしているため、混乱を招くコードであり、書かないことが好ましい- MDNには予約語ではないとあるが、一般的には予約語の一つとして解釈して支障ないと考える
- このコードはESLintのeslint:recommendedで検知されるため、通常であれば書かれることはない
なお、このコードは例示のために即時実行関数形式で記述しているが、必要がない限りこの形式での実装は避けたほうが問題が少なくなると思う。これは不必要なネストが生まれたり、スコープの混乱を生むためである。
- 投稿日:
2023-07-05にPrettier 3.0がリリースされて大分経ったので個人的に気になったところのまとめ。Highlights~Breaking Changesの部分だけ書いてます。Other Changesまで書くと長すぎるので…(Other Changesはざっと見た感じ概ね可読性が向上する感じの内容だと思いましたが、行が動くので書き方によってはコードや、行ベースで動作しているCIの検査が壊れる可能性が場合によってはありそうです。)
変更点
気になったところしか書いてないのでちゃんと見たい人は本家のリリースノートを見てください
日本語と西洋文字の間に半角スペースを挿入しない様に
Stop inserting spaces between Chinese or Japanese and Western characters
恐らく日本人的にはこれが今回最大の変更点になると思います。かなり賛否両論があると思いますが、私は肯定派です。むしろ半角スペースが入るようになって以来、これが来る日をずっと待ちわびていた。
今までは全角文字と半角文字の間に半角スペースが挿入されていましたが、これがなくなります。これは可読性の観点では悪くない処理だとは思うのですが、ドキュメントとしてみた場合に検索性が非常に悪く個人的に好きではありませんでした。
<!-- 入力値 -->
漢字
Alphabetsひらがな12345カタカナ67890
<!-- Prettier 2.8 -->
漢字 Alphabets ひらがな 12345 カタカナ 67890
<!-- Prettier 3.0 -->
漢字Alphabetsひらがな12345カタカナ67890
なお、この変更には後方互換性がなく、今までのように半角スペースを挿入することはできなくなります。
また、新しい方式に既存のドキュメントを合わせる場合、Prettierが挿入したこの半角スペースを自動で綺麗に除去するのは難しいと思います。探せばあるかもしれませんが、恐らくtextlintの除去ルールでは不完全だと思います。私が試した限りでは見出しやリンクタイトル辺りでは上手く機能してくれませんでした。ただ、ある程度はtextlintが解決してくれるので、大まかにはtextlintを使い、細かい部分は手修正していくのが無難かなと思います。
設定ファイルでESM形式をサポート
https://prettier.io/blog/2023/07/05/3.0.0.html#support-config-files-in-esm-13130httpsgithubcomprettierprettierpull13130-by-fiskerhttpsgithubcomfisker
今まではCJS形式で書く必要があったconfigファイルでESM形式がサポートされたようです。但し拡張子が.js
である場合はpackage.json
で{"type": "module"}
を指定する必要があります。
<!-- Prettier 2.8 -->
module.exports = {
// ...
}
<!-- Prettier 3.0 -->
export default {
// ...
};
trailingComma
設定の初期値をes5
からall
に変更
https://prettier.io/blog/2023/07/05/3.0.0.html#change-the-default-value-for-trailingcomma-to-all-11479httpsgithubcomprettierprettierpull11479-by-fiskerhttpsgithubcomfisker-13143httpsgithubcomprettierprettierpull13143-by-sosukesuzukihttpsgithubcomsosukesuzuki
これは正直あまり良く解っていないのですが、Trailing Commasを読んだ感じ、恐らく関数の仮引数の最後にカンマが入るかどうかだと思われます。つまり以下のような感じです。
<!-- 入力値 -->
function HelloWorld(greeting = "hello", greeted = '"World"', silent = false, onMouseOver) {
}
<!-- trailingComma: es5 -->
function HelloWorld(
greeting = "hello",
greeted = '"World"',
silent = false,
onMouseOver // カンマが入らない
) {}
<!-- trailingComma: all -->
function HelloWorld(
greeting = "hello",
greeted = '"World"',
silent = false,
onMouseOver, // カンマが入る
) {}
上記の結果はこちらのPlaygroundで確認できます。(--trailing-comma
の設定値をいじると比較できます)
個人的にはこの修正は余り歓迎できないですね…。というのも不要なカンマが入っていると引数の数を見間違えたり、ケアレスミスの原因になると思いますし、いたずらにコードが長くなるからです。
今まで深く考えたことがありませんでしたが、trailingComma
はnone
が例外がなく、統一感があり、コードが短くなり、ケアレスミスが減ると思うため無難だと思いました。none
にしている場合、単純に一番最後の要素にカンマが入らなくなります。
BabelパーサーからFlow構文のサポートを削除
https://prettier.io/blog/2023/07/05/3.0.0.html#remove-flow-syntax-support-from-babel-parser-14314httpsgithubcomprettierprettierpull14314-by-fiskerhttpsgithubcomfisker-thorn0httpsgithubcomthorn0
これによりPrettierの動作パフォーマンスが上がったようです。
デフォルトで.gitignore
のファイルを無視するように変更
https://prettier.io/blog/2023/07/05/3.0.0.html#ignore-gitignored-files-by-default-14731httpsgithubcomprettierprettierpull14731-by-fiskerhttpsgithubcomfisker
.prettierignore
ではなく.gitignore
を見るようになったようです。これもなんとも微妙な変更だと思います…。どっちかといえば.eslintignore
を見てもらいたいような気もするような…。従前のようにしたい場合はCLIのオプションに--ignore-path=.prettierignore
を渡してやると行けるようです。
関連記事
- 投稿日:
Web開発をしていると取り敢えずHTTPリクエストが取れる雑なモックサーバーが欲しくなることがあるので、その作り方。
確認環境
Env | Ver |
---|---|
OS | Windows 11 Pro |
WSL2 | - |
Distoribution | Ubuntu 20.04.4 LTS |
PHP | 8.0.29 |
nginx | 1.18.0 |
作り方
- Windows側の
hosts
に127.0.0.1 mock-server.test
を追加 /etc/nginx/conf.d
に以下の設定ファイルを置く
server {
listen 80;
client_max_body_size 100m;
server_name mock-server.test;
access_log /var/log/nginx/mock-server.access.log;
error_log /var/log/nginx/mock-server.error.log;
location ~ ^/.*$ {
rewrite ^/.*$ / break;
# リクエストパスの確認用
proxy_set_header X-Request-Path $request_uri;
proxy_pass http://127.0.0.1:8888;
}
}
sudo service nginx restart
- モックサーバーのコードを書いて適当な場所に置く
<?php
$server_json = json_encode($_SERVER);
file_put_contents('log', "$server_json\n", FILE_APPEND);
php -S localhost:8888
http://localhost:8888/hoge
にアクセスしログファイルに追記されることを確認- おわり
おまけ
$_SERVER
の中身をフィルタしたい時
<?php
$ignoreKeys = [
'DOCUMENT_ROOT',
'REMOTE_ADDR',
'REMOTE_PORT',
'SERVER_SOFTWARE',
'SERVER_PROTOCOL',
'SERVER_NAME',
'SERVER_PORT',
'REQUEST_URI',
'SCRIPT_NAME',
'SCRIPT_FILENAME',
'PHP_SELF',
'HTTP_HOST',
'HTTP_CONNECTION',
'HTTP_UPGRADE_INSECURE_REQUESTS',
'HTTP_USER_AGENT',
'HTTP_ACCEPT',
'HTTP_ACCEPT_ENCODING',
'HTTP_ACCEPT_LANGUAGE',
'HTTP_CACHE_CONTROL',
'REQUEST_TIME_FLOAT',
'REQUEST_TIME'
];
$buff = array_filter($_SERVER, function($key) use($ignoreKeys) {
return !in_array($key, $ignoreKeys);
}, ARRAY_FILTER_USE_KEY);
$server_json = json_encode($buff);
file_put_contents('log', "$server_json\n", FILE_APPEND);