※スマホ対応はしてません。

カテゴリー: JavaScript

React + TypeScriptのESLintルールをカスタマイズしたり、Airbnbのやつを導入するぞ。

カテゴリー: JavaScript

何もしないでそのままでも十分かもしれない。

先にまとめ

  • 最近の react-scripts は ESLint を含んでいるので別途インストール不要
  • 設定は .eslintrc.js を作成して extends: ['react-app'] する
  • Airbnb のルール設定を使う場合はもうちょい頑張る
  • 元々 create-react-app してない場合も同じ

Airbnb ルール利用時の手順概要

  1. ESLint 初期化
  2. ESLint の設定を更新
  3. コードを recommended な状態へ対応

想定環境

  • Node.js v10.16
  • react-scripts v3.1
  • TypeScript v3.5
  • ESLint v6.1
  • @typescript-eslint/eslint-plugin v1.13

React アプリの準備

create-react-app でテンプレートから作成します。既に存在するなら省略。

$ create-react-app my-greate-project --typescript --use-npm

--typescript で JS ではなく TS でファイル生成してくれます。

また create-react-app は標準で yarn を使います。--use-npm を設定すると npm になります。これはお好みで。

ESLint はインストール不要

react-scripts の依存パッケージとして ESLint 等がインストールされます。実際コンソールでエラーや警告を目にしたこともあるでしょう。

というわけで特に作業は不要です。

単体で実行する場合は TS の拡張子を指定

ESLint 単体で実行することも可能です。CI に組み込んでおくと良いかもね。

ただし、ESLint はそのまま実行すると JS ファイルだけを見に行きます。--ext で TS のファイルの拡張子 (extension) を見るよう指定します。

$ npx eslint src/ --ext .ts,.tsx

NPM スクリプトに登録

長いコマンドを今後何度も打たなくて良いよう package.json に書いておきます。

   "scripts": {
+    "lint": "eslint src --ext .ts,.tsx",
     "start": "react-scripts start",

そしたら今後はこう。

$ npm run lint

もし自動修正 --fix をやりたい場合はこうです。

$ npm run lint -- --fix

ESLint のルールをカスタマイズする

react-scripts 標準のルールセットは eslint-config-react-app というパッケージになっています。これも react-scripts に組み込まれているので、そのままで良ければ何もする必要はありません。

カスタマイズする場合は設定を用意します。ESLint の設定は .eslintrc.js というファイルで行います。他の選択肢もあるけど、たぶんこれが一番。

標準のルールセットを踏襲する場合、最小構成はこういう内容になります。

  • .eslintrc.js
module.exports = {
  extends: [
    'react-app',
  ],
  rules: {
  },
};

rules 内に好みのルールを追加してください。

元のルールを知る

create-react-app のリポジトリーの、 packages/eslint-config-react-app/index.js がそれです。

冒頭のコメントを引用、翻訳しておきます。

Inspired by https://github.com/airbnb/javascript but less opinionated.

We use eslint-loader so even warnings are very visible. This is why we only use “WARNING” level for potential errors, and we don’t use “ERROR” level at all.

In the future, we might create a separate list of rules for production. It would probably be more strict.

The ESLint browser environment defines all browser globals as valid, even though most people don’t know some of them exist (e.g. name or status). This is dangerous as it hides accidentally undefined variables. We blacklist the globals that we deem potentially confusing. To use them, explicitly reference them, e.g. window.name or window.status.

https://github.com/airbnb/javascript にインスパイアされつつもうちょいマイルドです。

eslint-loader を使用しているため、警告であっても非常に可視的です。そのため潜在的なエラーにも “WARNING” レベルを利用しており、”ERROR” レベルは全く利用していません。

将来的には本番環境用にまた別のルールリストを作成するかもしれません。そちらはより厳格になるはずです。

ESLint の browser 環境(訳者註:設定の env のこと)は全てのブラウザーグローバルを valid と定めており、それにはほとんどの方がその存在に気づいていないもの(例えば name や status)も含まれています。これは非常に危険であり、未定義の値を気づかないうちに隠蔽してしまう可能性があります。我々が潜在的に不確かであると判断したグローバルはブラックリストに登録しました。それらを利用する場合は明示的に参照してください。(例:window.name、window.status)

『警告であっても非常に可視的』の件は、”ERROR” レベルを利用するとリントエラーの際に(コンパイルエラー等と同様に)画面いっぱいにエラーが出てくるようになるのがアレだって話です。

最後の紛らわしいやつらブラックリストはこちら。

ESLint 設定を初期化

Airbnb のやつがたぶん今一番人気のあるルールセットかなと思うんですが、こちらを利用する場合は、react-scripts は忘れて ESLint の初期化機能を使うのが良いかなと思います。

というわけで eslint --init で、対話形式で初期設定をします。(npm ではなく yarn でやっている方は、最後の “Would you like to install them now with npm?” は “No” にして、その後 yarn install してください。)

$ npx eslint --init
? How would you like to use ESLint?
❯ To check syntax, find problems, and enforce code style

? What type of modules does your project use?
❯ JavaScript modules (import/export)

? Which framework does your project use?
❯ React

? Where does your code run?
❯ Browser

? How would you like to define a style for your project?
❯ Use a popular style guide

? Which style guide do you want to follow?
❯ Airbnb (https://github.com/airbnb/javascript)

? What format do you want your config file to be in?
❯ JavaScript

? Would you like to install them now with npm?
❯ Yes
...

これで package.json やロックファイルが更新され、.eslintrc.js が生成される。

(もう一度言うけど、yarn の人は yarn install をお忘れなく。npm の人は大丈夫。)

このタイミングでいったん git add しちゃうのお勧めします。今後はこの自動生成されたファイルを手動で更新していくので。

ESLint の設定を更新

eslint --init で生成された .eslintrc.js に設定を足す。(途中の @@ -20,6 +22,26 @@ は中略のことと思ってください。)

diff --git a/.eslintrc.js b/.eslintrc.js
@@ -2,9 +2,11 @@ module.exports = {
   env: {
     browser: true,
     es6: true,
+    jest: true,
   },
   extends: [
     'airbnb',
+    'plugin:@typescript-eslint/recommended',
   ],
   globals: {
     Atomics: 'readonly',
@@ -20,6 +22,26 @@ module.exports = {
   plugins: [
     'react',
   ],
+  settings: {
+    'import/resolver': {
+      node: {
+        extensions: ['.js', '.jsx', '.ts', '.tsx'],
+      },
+    },
+  },
   rules: {
+    '@typescript-eslint/indent': [
+      'error',
+      2,
+    ],
+    '@typescript-eslint/prefer-interface': 'off',
+    'react/jsx-filename-extension': [
+      'error',
+      { extensions: ['.jsx', '.tsx'] },
+    ],
+    'react/prop-types': 'off',
+    'spaced-comment': [
+      'error',
+      'always',
+      { markers: ['/ <reference'] },
+    ],
   },
};

TS と JS とを混在させているプロジェクトの場合、'react/prop-types': 'off', は overrides の方に書くと良いでしょう。詳細は ESLint のサイトで。

初期状態から recommended な状態へ対応

頑張ってぽちぽち直していきましょう。特に serviceWorker.ts は大量に出てきてビビるんだけど、実はエラーの種類は多くないので、落ち着いてやれば大丈夫です。

src/App.tsx

例としてこいつだけ修正内容を載せておきます。元の状態は create-react-app で生成した直後のやつです。

diff --git a/src/App.tsx b/src/App.tsx
@@ -1,14 +1,19 @@
-import React from 'react';
+import React, { ReactElement } from 'react';
 import logo from './logo.svg';
 import './App.css';
 
-const App: React.FC = () => {
+// eslint-disable-next-line arrow-body-style
+const App: React.FC = (): ReactElement => {
   return (
     <div className="App">
       <header className="App-header">
         <img src={logo} className="App-logo" alt="logo" />
         <p>
-          Edit <code>src/App.tsx</code> and save to reload.
+          Edit
+          {' '}
+          <code>src/App.tsx</code>
+          {' '}
+          and save to reload.
         </p>
         <a
           className="App-link"
@@ -21,6 +26,6 @@
       </header>
     </div>
   );
-}
+};
 
 export default App;

arrow-body-style は、今後開発を進めたらきっと {} が必要になるので、一時的に無効化しています。必要ないならいいです。

ESLint のエラーと対処

今回の作業で出会うかもしれないエラーです。

No files matching ‘src’ were found.

検証対象のファイルがひとつもない。(対象外のファイルはあるかもしれない。)

ESLint はそのままでは JS ファイルだけを探すので、「NPM スクリプト準備」の項でやったように --ext で TS の拡張子を設定してやる。

Unable to resolve path to module ‘./App’. (import/no-unresolved)

import しているファイルが見つからない。

ESLint はそのままでは JS ファイルだけを探すので、「ESLint の設定を更新」の項でやったように settings['import/resolver'].node.extensions で TS の拡張子を設定してやる。

ちなみに ./App.tsx のようにした場合は TS のコンパイラーに怒られる。

An import path cannot end with a ‘.tsx’ extension. Consider importing ‘./App’ instead.
ts(2691)

JSX not allowed in files with extension ‘.tsx’ (react/jsx-filename-extension)

JSX の記法が、許可されていない拡張子のファイルで利用されている。

.jsx じゃないと使えないようになっているので、.tsx も許可する。「ESLint の設定を更新」を参照。

よくわかんないけどこれ recommended に入らないかな。

Missing return type on function. (@typescript-eslint/explicit-function-return-type)

関数の戻り値が明示されていない。

なくても TS として正しいけれど、’plugin:@typescript-eslint/recommended’ は明示を推奨している。

React.FC 戻り値は ReactElement 。「初期状態から recommended な状態へ対応」を参照。

他はだいたい () => … を (): void => … にすれば解決する。そうでないものはちゃんと実装されている戻り値を確認しよう。

Unexpected block statement surrounding arrow body; move the returned value immediately after the =>. (arrow-body-style)

アロー関数で return を省略可能なのにされていない。

() => { return 123; } を () => 123 にする。

App のやつは、開発を進めたらきっと {} が必要になるので一時的に無効化しておけば良いかなと思う。「初期状態から recommended な状態へ対応」を参照。

Expected exception block, space or tab after ‘//’ in comment. (spaced-comment)

行コメント開始の記号 // の直後に空白が置かれていない。

ただ TS では Triple-Slash Directives ↓というのがあって、/// を使ってコンパイラーへ指示 (directive) を与えることができる

具体的には src/react-app-env.d.ts がそれです。

/// <reference types="react-scripts" />

この /// がこのルールと衝突するので、例外として設定してやる必要がある。「ESLint の設定を更新」を参照。

‘checkValidServiceWorker’ was used before it was defined. (@typescript-eslint/no-use-before-define)

関数等が定義より先に利用されている。

順序を変えるか、オフにしちゃっても良いかも。

Assignment to property of function parameter ‘registration’. (no-param-reassign)

受け取った引数(あるいはそのプロパティ)を上書きしている。

serviceWorker.ts の場合は特に問題ないので、その行は無視する。

+      // eslint-disable-next-line no-param-reassign
       registration.onupdatefound = (): void => {

Use an interface instead of a type literal. (@typescript-eslint/prefer-interface)

type より interface を使おうってやつ。

設定でオフにしちゃおう。「ESLint の設定を更新」を参照。

というのも、「別に type でよくね? むしろ type の方がよくね?」というのがどうやら最近の流れなので。実際 @typescript-eslint/eslint-plugin の次のメジャーバージョン v2 では recommended から外れるようです。

Unexpected console statement. (no-console)

デバッグ用コードである console.log() 等が残っている。

故意に残す場合、その行を無視する。

+// eslint-disable-next-line no-console
 console.log('Here');

serviceWorker.ts の場合は全体的に出していきたい感じ?っぽいので、ファイル全体を無効化することにする。

+/* eslint-disable no-console */

‘xxx’ is missing in props validation (react/prop-types)

React の props の検証が行われていない。

TS プロジェクトの場合はコンパイラーが静的に検証してくれるので、オフにする。「ESLint の設定を更新」を参照。

おまけ:VS Code のプラグイン設定

ESLint を CLI から毎回実行するより、エディター上で確認して対応していく方が楽です。

VS Code の場合はプラグイン導入で実現できます。

TypeScript に対応させるには設定が必要です。VS Code の設定 JSON ファイルを開いて(Ctrl + , → 右上 “{}” ボタン)、こんな設定。

{
…
    "eslint.autoFixOnSave": true,
    "eslint.validate": [
        "javascript",
        "javascriptreact",
        {"language": "typescript", "autoFix": true },
        {"language": "typescriptreact", "autoFix": true }
    ],
…

“autoFix” はファイル保存時に自動で修正(できる場合に)してくれる機能です。好みだけども ON にしとくと良いと思います。TS の場合はこう↑書いておかないと利かない。

[解決済み] TypeScript が新しすぎる警告

(2019/08/11 追記)最新版で解決済み。

こんなん言われる言われていた。

WARNING: You are currently running a version of TypeScript which is not officially supported by typescript-estree.

You may find that it works just fine, or you may not.

SUPPORTED TYPESCRIPT VERSIONS: >=3.2.1 <3.5.0

YOUR TYPESCRIPT VERSION: 3.5.3

Please only submit bug reports when using the officially supported version.

よくわからない。既に解決済みのようだけど。

The update was merged on 6 Jun as you can see above, and it was released to the latest tag on npm on the 9th of June: https://github.com/typescript-eslint/typescript-eslint/releases/tag/v1.10.1

使ってるのは v1.13 なんだけどなあ。

おわり

linter は早めに入れるのがお得です。

更新履歴

  • 2019/08/18
    • eslint-config-react-app を利用する方法を追加
    • 上記に合わせ記事タイトル及び全体の構成を変更
  • 2019/08/11
    • ESLint 及び @typescript-eslint/eslint-plugin のインストール作業を削除し、代わりに不要である旨記述
    • plugin:react/recommended を削除
    • 不要な TypeScript 系の設定を .eslintrc.js から削除
    • react-scripts を v6.0 から v6.1 へ更新
    • 不要になった @typescript-eslint/prefer-interface を削除
    • 「TypeScript が新しすぎる警告」が最新版で解決済みである旨記述
    • react/prop-types の対処を追加
  • 2019/08/06
    • 公開

参考

npm installやnpm ciがlength of undefinedで失敗するやつ、name設定で解決した。

カテゴリー: JavaScript

先に結論

エラー:

  • Cannot read property ‘length’ of undefined

条件:

  • npm v6.7+
  • package.json に postinstall 等があり、かつ name がない場合
  • npm install や npm ci 実行時

対処:

  • とりあえず package.json で name の設定すれば通る

あらすじ

TravisCI の自動試験で失敗の通知。

今まで Node.js v10.15 だったのが v10.16 になって失敗するようになった。過去に通ったやつも再試行すると失敗する。ひゃあ。

$ npm ci 
npm ERR! Cannot read property 'length' of undefined

debug.log はこんな感じ。

...
2533 info lifecycle eslint-config-airbnb-base@13.0.0~install: eslint-config-airbnb-base@13.0.0
2534 info lifecycle eslint-config-airbnb-base@13.0.0~postinstall: eslint-config-airbnb-base@13.0.0
2535 info lifecycle undefined@undefined~install: undefined@undefined
2536 info lifecycle undefined@undefined~postinstall: undefined@undefined
2537 verbose stack TypeError: Cannot read property 'length' of undefined
2537 verbose stack     at _incorrectWorkingDirectory (/home/ginpei/.nvm/versions/node/v10.16.0/lib/node_modules/npm/node_modules/npm-lifecycle/index.js:100:60)
2537 verbose stack     at /home/ginpei/.nvm/versions/node/v10.16.0/lib/node_modules/npm/node_modules/npm-lifecycle/index.js:72:44
2537 verbose stack     at /home/ginpei/.nvm/versions/node/v10.16.0/lib/node_modules/npm/node_modules/npm-lifecycle/index.js:203:12
2537 verbose stack     at /home/ginpei/.nvm/versions/node/v10.16.0/lib/node_modules/npm/node_modules/graceful-fs/polyfills.js:285:20
2537 verbose stack     at FSReqWrap.oncomplete (fs.js:154:5)
2538 verbose cwd /mnt/c/Users/ginpei/projects/webextension-pomodoro
2539 verbose Linux 4.4.0-17763-Microsoft
2540 verbose argv "/home/ginpei/.nvm/versions/node/v10.16.0/bin/node" "/home/ginpei/.nvm/versions/node/v10.16.0/bin/npm" "ci"
2541 verbose node v10.16.0
2542 verbose npm  v6.9.0
2543 error Cannot read property 'length' of undefined
2544 verbose exit [ 1, true ]

undefined@undefined とは……。

原因

手元で動かして追ってみる。

ということで、落ちる行まで追えた。

npm の実装は見たことないので細かいことわからないけど、ここの pkg.name.length が大丈夫なら解決しそう。

あと postinstall 削っても動いたので、ライフサイクル回りが npm v6.9 までに何か変わったのかも。

対処

というわけで、package.json で name を追加するとビルドが通るようになった。

本当なら報告したいけど Issue 作成は開かれてないし、この機に触ってみて PR 投げて貢献しようかな……と思って一月経ちました。

Reactで既存HTMLのプロパティを受け取るのにComponentPropsWithRefが使える?

カテゴリー: JavaScript

話題になっているのを全然みかけないんだけど、皆なんかもっと良いやり方やってんの? こういうことしないの?

あとコードハイライトなくてごめんね……。

先にまとめ

  • TypeScript の話
  • 既存 HTML ほとんどそのままのコンポーネントで、本来の props をそのまま受け取りたい場面
  • props: React.ComponentPropsWithRef<'span'> みたいにやれるっぽい
  • ドキュメントには載ってない
  • 定義を追っかけてみたけどたぶんきっと大丈夫そう
  • よくわかってないです

こんな風にして使っている。

export type HtmlProps<T extends ElementType> =
  React.ComponentPropsWithRef<T>;

export type HtmlComponent<T extends ElementType> =
  React.FC<HtmlProps<T>>;

後者は毎回書くのが面倒で追加したけど、ない方がコードは読みやすいかもしれない。

これを使って、特定のクラス名を付けただけのHTML要素を返すコンポーネントの例。

export const NiceButton: HtmlComponent<'button'> = (props) => (
  <button
    {...props}
    className={`NiceButton ${props.className || ''}`}
  />
);
<NiceButton>Niceですね~</NiceButton>

もうちょい複雑なコンポーネントの例。

interface NiceCheckboxProps extends HtmlProps<'input'> {
  label: string;
}

export const NiceCheckbox: React.FC<NiceCheckboxProps> = (props) => {
  const { className = '', label, ...inputProps } = props;
  return (
    <label className={`NiceCheckbox ${className}`}>
      <input {...inputProps} className="NiceCheckbox-input" type="checkbox" />
      <span className="NiceCheckbox-label">{label}</span>
    </label>
  );
};
<NiceCheckbox
  checked={agreed}
  label="ナイスであることに同意します"
  onClick={onAgreeClick}
/>

場面

ほぼ HTML

上に書いた、ああいう「ほとんどただの HTML 要素なんだけどちょっと追加とか組み合わせとかがあって、方々で使うから名前を付けて簡単に使いまわしたい」ような場面です。

CSS フレームワークみたいなノリだと思うんだけど、React でやるからには毎回クラス名書くんじゃなくて、そのクラス名を付けてくれるコンポーネントを使うようにしたいなと。

HTML同様の型が欲しい

例えば <input> だと value を持ってたりするじゃないですか。<span> にはないじゃないですか。そういうのを、補完で出したり型確認で弾いたりしたいなと思っておりました。

見つけた

React.prop まで入力したところの補完で出てきた候補の中からそれっぽいのを眺めてるときに見つけました。

React 公式サイトで検索しても出てこない、と思ったけど React プロジェクトじゃなくて @typed のだから当たり前か。ありがてえ。

定義を追ってみる

ここから先はTS初級者がうんうん唸ってるだけです。誰かに教えてほしい……。

なお @types/jest のバージョンは 24.0.12 でした。

ComponentPropsWithRef

type ComponentPropsWithRef<T extends ElementType> =
    T extends ComponentClass<infer P>
        ? PropsWithoutRef<P> & RefAttributes<InstanceType<T>>
        : PropsWithRef<ComponentProps<T>>;

へ、へえー……。

ElementType

まずジェネリクスの方。

type ElementType<P = any> =
    {
        [K in keyof JSX.IntrinsicElements]: P extends JSX.IntrinsicElements[K] ? K : never
    }[keyof JSX.IntrinsicElements] |
    ComponentType<P>;

これはあれだな、IntrinsicElements にあればその型('a' なら <a> 用の props)を使うぞってことだな。

IntrinsicElements

interface IntrinsicElements {
    // HTML
    a: React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>;
    abbr: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
    address: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
    area: React.DetailedHTMLProps<React.AreaHTMLAttributes<HTMLAreaElement>, HTMLAreaElement>;
    article: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
…

究極的にはここの右側 React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement> を毎回コピペして使えば使えるんだな。したくないけど。

JSX で書いた <span> とかを VS Code で Ctrl + クリックすると出てくるのもこいつ。

ComponentClass

こっちがわからない。

interface ComponentClass<P = {}, S = ComponentState> extends StaticLifecycle<P, S> {
    new (props: P, context?: any): Component<P, S>;
    propTypes?: WeakValidationMap<P>;
    contextType?: Context<any>;
    contextTypes?: ValidationMap<any>;
    childContextTypes?: ValidationMap<any>;
    defaultProps?: Partial<P>;
    displayName?: string;
}

<infer P> との組み合わせで、React.Component 継承のクラスを与えると良い感じに props の型を抽出してくれる系かなと思ったんだけどそんなことなかった。

ちなみに infer ってここ(上の ComponentPropsWithRef のとこ)で初めて見ました。

PropsWithRef

Indent Hadouken みがある。

/** Ensures that the props do not include string ref, which cannot be forwarded */
type PropsWithRef<P> =
    // Just "P extends { ref?: infer R }" looks sufficient, but R will infer as {} if P is {}.
    'ref' extends keyof P
        ? P extends { ref?: infer R }
            ? string extends R
                ? PropsWithoutRef<P> & { ref?: Exclude<R, string> }
                : P
            : P
        : P;

たぶん大丈夫でしょう

知らんけど。

とりあえず動いてはいる。うーんちゃんと理解して使いたい……。

おしまい

VS Code で F12 を駆使して追っかけてみました。ほんとべんり。

よくわからないけどとりあえず動いているっぽいのでこのまま使おう。趣味プロジェクトだし、まあーそのうちTS力が上がれば完全理解できるでしょう。

あとそういえばこのブログテーマも更新して、TypeScript のハイライトも入れたいなあ。

Google Chromeで音声認識する例。

カテゴリー: JavaScript

意識しないとすぐTwitterに書いて終わりにしてしまうので、気負わず軽い気持ちで書きます。

諸事情により音声認識APIを試しているんだけど、ウェブ標準でもそういうAPIが準備中であるのに気づきました。現状のGoogle Chromeでもそこそこ動くようです。せっかくなので(Chromeで)動くやつを用意してみました。

ginpei/speech-recognition-example

まだ実験的な機能だし、実際動きも微妙なところなので、やっぱりGoogleなりMicrosoft AzureなりのAPI使う方が良さそう。無料枠あるし。

基本的なコード

// 作成
const sr = new window.SpeechRecognition();

// 完了時に結果を表示
sr.addEventListener('result', (event) => {
  const text = event.results[0][0].transcript;
  console.log(text);
});

// 開始
sr.start();

音が止まると音声認識も自動で終了する様子。

各ブラウザー

Chrome

接頭辞付きの webkitSpeechRecognition で動く。まだ完璧ではない様子。

あとそういえば昔 <input x-webkit-speech> みたいなのあったなあ、みたいな記憶。

Firefox

まだ対応していない。コードは mozSpeechRecognition に対応しておいたので、実装されたらきっと動くことでしょう。知らんけど。

Edge

ちょうど最近開発版が出た、中身がChromiumの新Edgeはまだ動きが微妙な感じでした。

同じ webkit 接頭辞なのに動いたり動かなかったりは困るなあ。いや今は開発版だから良いんだけど。

Safari

試してないけど未対応らしい。

JPEG/Exif画像の回転情報を取れるやつ作った。

カテゴリー: JavaScript

新年明けましておめでとうございます。作ったよ。

インストール。

$ npm install @ginpei/exif-orientation

利用例。

import * as exif from '@ginpei/exif-orientation';
 
const orientation = await exif.getOrientation(fileOrBuffer);
console.log(
  `${orientation.rotation} degree,`,
  orientation.flipped ? 'flipped' : 'not flipped',
);

上記のものではないが実行例。

$ node from-node.js sample-images/090-flipped-5.jpg
Read image from: sample-images/090-flipped-5.jpg
Rotation: 90 degree, Flipped: true.

検索するといかにもな名前のやつとかが複数出てくるんだけど、先行事例に対する利点は特にないです、たぶん。

作りたいので作った感じのやつです。ちゃんと動くと思うけど。

Scoped package

名前が取られてたけど作っちゃったし変えるのもなあと思って、自分の名前 “@ginpei” 付きの形での公開としました。

READMEにも書いたけど、TypeScriptの場合は --moduleResolution node の設定が必要です。へえそうなんだ、知らなかった。

エラー例。

src/index.ts:1:23 - error TS2307: Cannot find module '@ginpei/exif-orientation'.

1 import * as exif from '@ginpei/exif-orientation';
                        ~~~~~~~~~~~~~~~~~~~~~~~~~~


Found 1 error.

Exif仕様

こちらのPDFを大いに参考にしています。感謝感謝。

ところでExifて日本発祥の規格なんだね。

富士フイルムが開発し、当時の日本電子工業振興協会 (JEIDA)で規格化

画像の回転

とかそういうのは、一切しないです。調査するまで。

やるならCanvasが良いのかな。いやJPEGの画素情報の位置を回転的に動かす方が再圧縮しないから綺麗そう。

一時的ならCSSでやるのも手。よし transform: matrix() 使おうぜ!

JPEG以外

Exifを他の規格の画像にも使えるらしいんだけど、JPEGのみの対応です。見ないし。

おしまい

本年もよろしくお願い致します。