20190524のReactに関する記事は8件です。

Puppeteer をヘッドフルにしてあげた

Puppeteer君、きみはよくがんばった。褒美に画面をあたえよう。

ということで、スクレイピングやE2Eテストなど裏方で活躍している Headless Chrome Node API の Puppeteer に特に理由もなく Web フロントエンドを実装しました。

動かしてみよう

デモサイト(重すぎるのであまりおすすめではありません)
https://boiyaa-headful-puppeteer.appspot.com

または、

https://github.com/boiyaa/headful-puppeteer
をクローンして、

$ npm i
$ npm run dev

して、 http://localhost:8080 にアクセス。

ブラウザの中のブラウザ風UIの中のアドレスバーに入力されている URL の画面が表示されますが、HTMLではなく、 Pupeteer で叩いて取得したスナップショット画像を表示しています。
このアドレスバーに例えば GitHub の URL を入力すれば GitHub のスナップショット画像に切り替わります。
また、画像ですが、画像内のマウスクリック座標を渡して Pupeteer が代わりにリンクをクリックして遷移するように仕上げてみました。
headful1.gif

クリックを受け取れる機能を利用してちょっと試してみます。
アドレスバーの左に、この Headful Puppeteer に遷移するホームボタンを用意しました。
これを押すと、Puppeteer で Headful Puppeteer を開いてその中の Puppeteer が Puppeteer のWebサイトにアクセスしている状態になります。
その画像の中のホームボタンを押す、を繰り返しただけ増殖します。
headful2.gif

ん〜実にヘッドフル。

Chrome を意気揚々と操っている Puppeteer 自身もまた何者かに操られている、という社会派アートができました。

以上です。

やったこと

sequence.png

画面の実装

import React, { useState, useEffect } from "react";
import axios from "axios";

type ReactFC<P> = React.FC<P> & { getInitialProps?: (context: { req: { query: { url: string } } }) => Promise<P> };

interface Props {
  initialUrl: string;
}

type Url = string | undefined;

type Position = {
  x: number;
  y: number;
} | null;

const Index: ReactFC<Props> = ({ initialUrl }) => {
  const [mouseOffset, setMouseOffset] = useState<Position>(null);
  const [inputValue, setInputValue] = useState<Url>(
    initialUrl ? initialUrl : "https://developers.google.com/web/tools/puppeteer/"
  );
  const [query, setQuery] = useState<{
    url: Url;
    position: Position;
  }>({ url: inputValue, position: mouseOffset });
  const [imgSrc, setImgSrc] = useState<string | null>(null);

  useEffect(() => {
    if (!query.url) {
      return;
    }

    // (2) 入力されたURL、画面サイズを渡す
    // (8) 現在のURL、画面サイズ、クリックした座標を渡す
    const url = query.url;

    const browserWindow = document.getElementById("window");
    const width = browserWindow ? browserWindow.clientWidth.toString() : "";
    const height = browserWindow ? browserWindow.clientHeight.toString() : "";

    const params = query.position
      ? new URLSearchParams({
          url,
          width,
          height,
          "position[x]": query.position.x.toString(),
          "position[y]": query.position.y.toString()
        })
      : new URLSearchParams({ url: query.url, width, height });

    const browser = `/api/browse?${params.toString()}`;

    axios.get(browser).then(response => {
      // (6) (12) ウィンドウにスナップショットを表示
      setImgSrc(response.data.screenshot);
      setInputValue(response.data.url);

      history.pushState(null, "", `?url=${encodeURIComponent(url)}`);
    });
  }, [query]);

  return (
    <>
      <section>
        <h1>Headful Puppeteer</h1>

        <div id="container">
          <div id="address-bar">
            <button
              onClick={() => {
                setQuery({ url: "http://localhost:8080/", position: null });
              }}
            >
              ?
            </button>
            {/* (1) アドレスバーにURLを入力 */}
            <input
              type="text"
              value={inputValue}
              onChange={event => setInputValue(event.target.value)}
              onKeyDown={event => (event.keyCode === 13 ? setQuery({ url: inputValue, position: null }) : null)}
            />
          </div>

          {/* (7) スナップショット上のリンクをクリック */}
          <div
            id="window"
            onMouseMove={event => setMouseOffset({ x: event.nativeEvent.offsetX, y: event.nativeEvent.offsetY })}
            onClick={() => setQuery({ url: inputValue, position: mouseOffset })}
          >
            {imgSrc ? <img src={`data:image/png;base64,${imgSrc}`} /> : ""}
          </div>
        </div>
      </section>
    </>
  );
};

Index.getInitialProps = async (context: { req: { query: { url: string } } }) => ({ initialUrl: context.req.query.url });

export default Index;

APIの実装

Express のハンドラの中で Puppeteer を動かしています。
参考: Page クラスのドキュメント

import { Request, Response } from "express";
import puppeteer from "puppeteer";

const dev = process.env.NODE_ENV !== "production";

export default async (req: Request, res: Response) => {
  console.log(req.query);

  const browser = await puppeteer.launch({ args: ["--no-sandbox"] });
  const page = await browser.newPage();
  await page.setViewport({ width: parseInt(req.query.width, 10), height: parseInt(req.query.height, 10) });

  // (3) Puppeteerでアクセス
  await page.goto(req.query.url, { waitUntil: dev ? "networkidle2" : "networkidle0" });

  if (req.query.hasOwnProperty("position")) {
    // (9) Puppeteerでアクセスし、指定座標でマウスクリックし、画面遷移する
    await Promise.all([
      page.mouse.click(parseInt(req.query.position.x, 10), parseInt(req.query.position.y, 10)),
      page.waitForNavigation({ waitUntil: dev ? "networkidle2" : "networkidle0" })
    ]);
  }

  // (4) (10) スナップショット取得
  const screenshot = await page.screenshot({ encoding: "base64" });
  const url = page.url();

  await browser.close();

  // (5) (11) スナップショットとURL状態を返す
  res.json({ screenshot, url });
};

おすすめのデプロイ先

リポジトリの方に設定ファイル入れていますが、 Google App Engine がおすすめです。

同じ Google 製ということもあるし、「Puppeteer でのヘッドレス Chrome の使用」と公式ドキュメントで説明もありますのでサクッと動かす環境作れます。

まとめ

React Hooks コンポーネントのテストめんどそう

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

GatsbyのStaticQueryをどこで使うか

Gatsybyでのサイト構築で得た個人的見解を記します。

StaticQueryとは

コンポーネントにGraphQL経由でデータを渡す機能です。

これにより、今までページ生成用ファイルからしかGraphQLを呼び出せなかったものが、各コンポーネントからもGraphQLを呼び出すことができるようになりました。

公式→https://www.gatsbyjs.org/docs/static-query/

GatsbyとGraphQLの仕様

Gatsbyはデータ管理にGraphQLを用いています。

このGraphQLを呼び出すことができるのは、ページ生成用のファイル内からのみ可能です。

具体的には、pageディレクトリにあるファイルと、gatsby-node.jsでページ生成を設定したファイルのみになります。

仕様の問題点

通常Reactでは、親コンポーネントから子コンポーネントへデータを流す構造にします。
Gatsbyでも同様に、親から子へデータを流すことができます。

しかし、Gatsbyはページ生成用ファイルからしかGraphQLを使えないため、不自然な設計になることがあります。

それは、GraphQL経由で共有部分にデータを渡すときです。

例として、ヘッダー部分のカテゴリーリストについて考えます。画像はw3school.comのヘッダー部分です。
スクリーンショット 2019-05-24 16.36.25.png
図のようにヘッダー内のメニューバーへカテゴリーリストを渡したい時、通常Reactではヘッダーコンポーネントを親としてデータを取得させます。そのデータを、子であるメニューバーコンポーネントに渡す構造にするはずです(たぶん)。

しかし、Gatsbyではこれができません。
GatsbyがgraphQLからデータを取得できるのは、ページ生成を行うファイルからのみです。

ルートページ、ブログ記事ページ、カテゴリー別ページ...それぞれでGraphQLからカテゴリーリストを取得し、それをヘッダー/メニューバーコンポーネントまで流す必要があります。二度手間ですね。

もちろんこれでも動きますし、静的サイトジェネレーターなので、本番環境でパフォーマンスの低下があるわけでもありません。せいぜい、生成にほんの少し時間がかかるだけです。

しかし、きれいなコードを目指すなら、ヘッダーコンポーネントに直接データを渡したいはずです。

そんなときに役立つのがStaticQueryです。

StaticQueryの効果

StaticQueryを使えば、GraphQLからヘッダーコンポーネントに直接データを渡せます。
すると、各ページ生成用ファイルからヘッダー/メニューバーコンポーネントに関するデータ処理が消え、非常にわかりやすい構造が出来上がります。

このようにStaticQueryを使うと、データ処理が通常のReactの設計へより近づきます。

まとめ

以上より、共有するコンポーネントにデータを渡したいときは、StaticQueryを積極的に使うべきだと考えます。

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

React.unstable_ConcurrentModeは廃止について

はじめに

去年の11月にReact 16.x Roadmapが公開されて、次はConcurrent Modeの登場を待っている状態です。
少し前になりますが、これに関連して変更があったようです。

React.unstable_ConcurrentMode

Concurrent Modeを試すには二通りの方法がありました。上記の記事から抜粋します。

// Two ways to opt in:

// 1. Part of an app (not final API)
<React.unstable_ConcurrentMode>
  <Something />
</React.unstable_ConcurrentMode>

// 2. Whole app (not final API)
ReactDOM.unstable_createRoot(domNode).render(<App />);

この前者の方法が廃止されたとのことです。
詳しくは、 https://github.com/facebook/react/pull/15532 を参照してください。

部分的にConcurrent Modeを適用すると難しいケースがある上に、利用シーンも分からないということのようです。

今後は後者のcreateRootを使うことになります。

おわりに

自分のテストコードで複数の場所で使っているので、近いうちに修正しておかなければと思いました。

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

React.unstable_ConcurrentModeは廃止されたようです

はじめに

去年の11月にReact 16.x Roadmapが公開されて、次はConcurrent Modeの登場を待っている状態です。
少し前になりますが、これに関連して変更があったようです。

React.unstable_ConcurrentMode

Concurrent Modeを試すには二通りの方法がありました。上記の記事から抜粋します。

// Two ways to opt in:

// 1. Part of an app (not final API)
<React.unstable_ConcurrentMode>
  <Something />
</React.unstable_ConcurrentMode>

// 2. Whole app (not final API)
ReactDOM.unstable_createRoot(domNode).render(<App />);

この前者の方法が廃止されたとのことです。
詳しくは、 https://github.com/facebook/react/pull/15532 を参照してください。

部分的にConcurrent Modeを適用すると難しいケースがある上に、利用シーンも分からないということのようです。

今後は後者のcreateRootを使うことになります。

おわりに

自分のテストコードで複数の場所で使っているので、近いうちに修正しておかなければと思いました。

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

React.unstable_ConcurrentModeの廃止について

はじめに

去年の11月にReact 16.x Roadmapが公開されて、次はConcurrent Modeの登場を待っている状態です。
少し前になりますが、これに関連して変更があったようです。

React.unstable_ConcurrentMode

Concurrent Modeを試すには二通りの方法がありました。上記の記事から抜粋します。

// Two ways to opt in:

// 1. Part of an app (not final API)
<React.unstable_ConcurrentMode>
  <Something />
</React.unstable_ConcurrentMode>

// 2. Whole app (not final API)
ReactDOM.unstable_createRoot(domNode).render(<App />);

この前者の方法が廃止されたとのことです。
詳しくは、 https://github.com/facebook/react/pull/15532 を参照してください。

部分的にConcurrent Modeを適用すると難しいケースがある上に、利用シーンも分からないということのようです。

今後は後者のcreateRootを使うことになります。

おわりに

自分のテストコードで複数の場所で使っているので、近いうちに修正しておかなければと思いました。

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

『React』をかじりたい

よく聞くけど『React』ってなに?

っていう、忘れっぽい自分のための備忘メモです。
概要を知って、環境構築して、やってみる、までの間に参考にしたものをまとめています。

これ読んだ

  • Reactの概要を知りたい

【参考記事】Reactを使うとなぜjQueryが要らなくなるのか

:bulb: JavaScriptとHTML(?)が悪魔合体しているのが基本作法
:bulb: Reactは 仮想DOMと呼ばれる「DOMの設計図」 をベースに処理
:bulb: 機能的に関連するタグと動作を、名前付きでまとめて短く記述できるReactの記述方法

  • Reactの環境構築が知りたい

【参考記事1】React環境構築(初心者向け)

  1. reactとreact-domのインストール
  2. Webpackのインストール
    Webpackって?JSファイルをまとめる高機能なモジュールバンドラー。まとめることでウェブページのHTTPリクエストの数を減らしたり、高度なウェブアプリケーションの開発に役立つ (ここから引用)
  3. Babelのインストール
    Babelって?次世代のJavaScriptの標準機能を、ブラウザのサポートを待たずに使えるようにするNode.js製のツールです。次世代の標準機能を使って書かれたコードを、それらの機能をサポートしていないブラウザでも動くコードに変換(トランスパイル)します。(ここから引用)
  4. ブラウザリロードの実装
  5. index.htmlにコード追加

【参考記事2】正真正銘のReactだけの不純物なしでReact入門

感想

javascriptでDOM操作をごりごり書いてた時代は終わってたんだなぁ。
初心者でも使うのには抵抗ない感じ。環境構築さえしてしまえば。

ほんとは
  1. 読む
  2. Qiitaにまとめる
  3. 環境作ってミニマムでやってみる
  4. GITにあげる
までやりたい。
以上、Qiitaをうまいこと使っていきたいと思った初投稿でした。

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

Reactで実践!Schematics

自己紹介

icon

よろしくお願いします!


はじめに

Angular CLIで利用されているSchematics を使って、いつもの作業をいい感じに効率化できたので紹介させていただきます。

Angular CLIはこんな感じのやつです!使ったことある人は多いのではないでしょうか?

$ ng g component my-hero
installing component
  create src/app/my-hero/my-hero.component.css
  create src/app/my-hero/my-hero.component.html
  create src/app/my-hero/my-hero.component.spec.ts
  create src/app/my-hero/my-hero.component.ts
  update src/app/app.module.ts

AngularユーザーでなくてもSchematicsは使えるのでいつもの作業をスキャッフォルディグっちゃいましょう!


Schematicsって?

README.mdには下記のように書いてあります。

モダンウェブのスキャッフォルドライブラリです。

ファイルの作成、既存のファイルのリファクタリングや移動などができます。
とのこと。

そして説明には同様のライブラリとの違いとして下記を挙げています。

  • まさしく記述的
  • すべてがコミットされる準備ができるまで、実際のファイルシステムを変更しない
  • Schematicsでは副作用はありません

ところであのおじさん元気かなー

68747470733a2f2f662e636c6f75642e6769746875622e636f6d2f6173736574732f3137303237302f3739343530302f34316532643439382d656361322d313165322d386466352d3936326639666139346336332e706e67.png

できることはyeomanおじさんと似てますが、Schematicsには「お、いけてる」と思う点がたくさんありました。
実際に作ったものを紹介しながら良さも伝えていきたいと思います。


いつもの作業...

私が欲しかったものは

src/components/Header/
├── Header.jsx
├── Header.stories.js
├── Header.test.js
└── index.js

こんな感じのコンポーネントのワンセットを


コマンドでサクッと

  • 作りたいコンポーネント名
  • パス

を下記のように指定したら生成してくれて、

$ npm run gen -- --name=Header --path=src/components

あわよくば実行可能なストーリーブックとスナップショットテストが欲しい。


できる!

https://github.com/hand-dot/component-gen
component-gen.gif

npmで公開していますが、現状ほぼ自分のために作ったので悪しからず...


思った以上に簡単だった...!

初めて触ったんですが、自分のやりたいことのレベルならnpmに公開して別プロジェクトで使うまで2,3時間くらいで作れたので良かった。

もし同じようなことがやりたい人がいたら...と思い、やったことを下記で簡単に説明していきます。


セットアップ

  • schematicsをインストールして
  • 空のschematics cli「my-schematics」を作って
  • 移動します
$ npm install -g @angular-devkit/schematics-cli
$ schematics blank my-schematics
$ cd my-schematics

複数のSchematicを使いたい場合や、実装をみてみたい場合は

$ schematics schematic --name=my-schematics

でセットアップするといいかも。丁寧にコメントしてあってわかりやすいです。


srcの中のファイルはこんな感じ

src/
├── collection.json <-Schematicsの定義ファイル
└── my-schematics
    ├── index.ts
    └── index_spec.ts

ビルドして実行

$ npm run build
$ schematics .:my-schematics

テストもついてる

$ npm run test

一番簡単なファイルの作成

Hello world!という内容のhiというファイルを作ってみましょう。

import { Rule, SchematicContext, Tree } from "@angular-devkit/schematics";

export function mySchematics(_options: any): Rule {
  return (tree: Tree, _context: SchematicContext) => {
    tree.create("/hi", "Hello world!");
    return tree;
  };
}

Treeは変更のためのステージング領域。Treeにあんなことやこんなことをして返却して変更をリクエストする。

時間の関係上、機能は紹介しませんが

などを参考にしていただければと思います。(APIもシンプルなので良きです。)


Schematicsのここが好き!

Dry Run!
例えばさっきの一番簡単なファイルの作成を実行すると実際にファイルは作成されないが、下記のようなフィードバックがある。

$ schematics .:my-schematics
CREATE /hi (12 bytes)

--dry-run=false のオプションを渡すと本当にファイル作ります。テストも同様でsetUpやtearDownを書かなくていいため、ファイルを消して再実行!みたいな手間がない。

そして、下記のように

npm run build -- -w

でwatchしながら実装すればサクサク試せるので楽しかった。


Schematicsのここで困った...

$ schematics .:my-schematics
実行時の.:
これなに?ってなってシカトしてたらnpmパッケージにした後に他プロジェクトで実行できなくて少し迷った。

Helpにはサフィックスでセミコロンをつけろとのこと。

schematics [CollectionName:]SchematicName [options, ...]

.:の場合は同階層のpackage.jsonのschematicsプロパティからcollection.jsonを見つけて指定されたSchematicNameを実行する。
そして例としてあげた「my-schematics」をnpmで公開して別プロジェクトでnpm installした場合の実行は

$ schematics my-schematics:my-schematics

となる。

READMEにも CollectionにNPMパッケージを使用します。との記載があるが、
最初はCollectionName? SchematicName? え? となるので困った。

ちなみに実行時にCollectionNameに.: を使うとデフォルトがDry Runになる。


まとめ

  • 定型作業をサクッと効率化できる。
  • え?yeoman!?(プークスクス)ってならない。
  • え? Don't repeat yourself ですよ?(キャーカクイイ)ってなるかもしれない。
  • AngularのいいところがAngular以外で使えて嬉しい。
  • Schematicsの開発自体が結構楽しい。(ここ重要)

おわり

ご静聴ありがとうございました!
icon


参考にさせていただきました!

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

【開発を効率化しよう】Reactで実践!Schematics

schematicsを使っていつもの作業を効率化しよう!
Reactのコンポーネント、テスト、ストーリーブックの作成をスキャッフォルドしてみた!
component-gen.gif

自己紹介

icon

よろしくお願いします!


はじめに

Angular CLIで利用されているSchematics を使って、いつもの作業をいい感じに効率化できたので紹介させていただきます。

Angular CLIはこんな感じのやつです!使ったことある人は多いのではないでしょうか?

$ ng g component my-hero
installing component
  create src/app/my-hero/my-hero.component.css
  create src/app/my-hero/my-hero.component.html
  create src/app/my-hero/my-hero.component.spec.ts
  create src/app/my-hero/my-hero.component.ts
  update src/app/app.module.ts

AngularユーザーでなくてもSchematicsは使えるのでいつもの作業をスキャッフォルディグっちゃいましょう!


Schematicsって?

README.mdには下記のように書いてあります。

モダンウェブのスキャッフォルドライブラリです。

ファイルの作成、既存のファイルのリファクタリングや移動などができます。
とのこと。

そして説明には同様のライブラリとの違いとして下記を挙げています。

  • まさしく記述的
  • すべてがコミットされる準備ができるまで、実際のファイルシステムを変更しない
  • Schematicsでは副作用はありません

ところであのおじさん元気かなー

68747470733a2f2f662e636c6f75642e6769746875622e636f6d2f6173736574732f3137303237302f3739343530302f34316532643439382d656361322d313165322d386466352d3936326639666139346336332e706e67.png

できることはyeomanおじさんと似てますが、Schematicsには「お、いけてる」と思う点がたくさんありました。
実際に作ったものを紹介しながら良さも伝えていきたいと思います。


いつもの作業...

私が欲しかったものは

src/components/Header/
├── Header.jsx
├── Header.stories.js
├── Header.test.js
└── index.js

こんな感じのコンポーネントのワンセットを


コマンドでサクッと

  • 作りたいコンポーネント名
  • パス

を下記のように指定したら生成してくれて、

$ npm run gen -- --name=Header --path=src/components

あわよくば実行可能なストーリーブックとスナップショットテストが欲しい。


できる!

https://github.com/hand-dot/component-gen
component-gen.gif

npmで公開していますが、現状ほぼ自分のために作ったので悪しからず...


思った以上に簡単だった...!

初めて触ったんですが、自分のやりたいことのレベルならnpmに公開して別プロジェクトで使うまで2,3時間くらいで作れたので良かった。

もし同じようなことがやりたい人がいたら...と思い、やったことを下記で簡単に説明していきます。


セットアップ

  • schematicsをインストールして
  • 空のschematics cli「my-schematics」を作って
  • 移動します
$ npm install -g @angular-devkit/schematics-cli
$ schematics blank my-schematics
$ cd my-schematics

複数のSchematicを使いたい場合や、実装をみてみたい場合は

$ schematics schematic --name=my-schematics

でセットアップするといいかも。丁寧にコメントしてあってわかりやすいです。


srcの中のファイルはこんな感じ

src/
├── collection.json <-Schematicsの定義ファイル
└── my-schematics
    ├── index.ts
    └── index_spec.ts

ビルドして実行

$ npm run build
$ schematics .:my-schematics

テストもついてる

$ npm run test

一番簡単なファイルの作成

Hello world!という内容のhiというファイルを作ってみましょう。

import { Rule, SchematicContext, Tree } from "@angular-devkit/schematics";

export function mySchematics(_options: any): Rule {
  return (tree: Tree, _context: SchematicContext) => {
    tree.create("/hi", "Hello world!");
    return tree;
  };
}

Treeは変更のためのステージング領域。Treeにあんなことやこんなことをして返却して変更をリクエストする。

時間の関係上、機能は紹介しませんが ←2019/05/25追記: 次のセクションにて解説します。

などを参考にしていただければと思います。(APIもシンプルなので良きです。)


今回作成したSchematicsの簡単な解説

やりたいことはReactのテンプレートを用意し、ファイル名や一部の文字を置き換え、任意の場所にコピーするだけなので単純です。

公式のREADME.mdのTemplatingを参考にテンプレートを呼び出す例として下記の処理を見てみましょう。

files/__name@dasherize__.ts
export class <%= classify(name) %> {
}
index.ts
import { strings } from '@angular-devkit/core';
import {
  Rule, SchematicContext, SchematicsException, Tree,
  apply, mergeWith, template, url,
} from '@angular-devkit/schematics';
import { Schema as ClassOptions } from './schema';

export default function (options: ClassOptions): Rule {
  return (tree: Tree, context: SchematicContext) => {
    if (!options.name) {
      throw new SchematicsException('Option (name) is required.');
    }

    const templateSource = apply(
      url('./files'),
      [
        template({
          ...strings,
          ...options,
        }),
      ]
    );

    return mergeWith(templateSource);
  };
}

なんとなく予想つきますが、テンプレートのプレースホルダを埋めます。

template関数を見てみると渡しているのは...options...stringsです。

optionsに関してはテンプレート内<%= classify(name) %>とファイル名の__name@dasherize__nameで渡した何かしらの文字列に置き換えます。

stringsは使用されているdasherizeclassify関数をテンプレート内とファイル名に提供しています。(ファイル名に関数を適応する場合は@をつける)

なのでoptions.namefooBarを渡して実行した場合、実行したディレクトリに下記のようなファイルが作成されます。

foo-bar.ts
export class FooBar {
}

これを理解しておくと私がやったことはなんとなく想像ができるのではないでしょうか?

src/
├── collection.json
└── component-gen
    ├── files
    │   └── react
    │       ├── __name@classify__.jsx
    │       ├── __name@classify__.stories.js
    │       ├── __name@classify__.test.js
    │       └── index.js
    └── react
        ├── index.d.ts
        ├── index.ts
        ├── index_spec.d.ts
        └── index_spec.ts

ソースコードは(こちら)[https://github.com/hand-dot/component-gen]でご覧いただけます。


Schematicsのここが好き!

Dry Run!
例えばさっきの一番簡単なファイルの作成を実行すると実際にファイルは作成されないが、下記のようなフィードバックがある。

$ schematics .:my-schematics
CREATE /hi (12 bytes)

--dry-run=false のオプションを渡すと本当にファイル作ります。テストも同様でsetUpやtearDownを書かなくていいため、ファイルを消して再実行!みたいな手間がない。

そして、下記のように

npm run build -- -w

でwatchしながら実装すればサクサク試せるので楽しかった。


Schematicsのここで困った...

$ schematics .:my-schematics
実行時の.:
これなに?ってなってシカトしてたらnpmパッケージにした後に他プロジェクトで実行できなくて少し迷った。

Helpにはサフィックスでセミコロンをつけろとのこと。

schematics [CollectionName:]SchematicName [options, ...]

.:の場合は同階層のpackage.jsonのschematicsプロパティからcollection.jsonを見つけて指定されたSchematicNameを実行する。
そして例としてあげた「my-schematics」をnpmで公開して別プロジェクトでnpm installした場合の実行は

$ schematics my-schematics:my-schematics

となる。

READMEにも CollectionにNPMパッケージを使用します。との記載があるが、
最初はCollectionName? SchematicName? え? となるので困った。

ちなみに実行時にCollectionNameに.: を使うとデフォルトがDry Runになる。


まとめ

  • 定型作業をサクッと効率化できる。
  • え?yeoman!?(プークスクス)ってならない。
  • え? Don't repeat yourself ですよ?(キャーカクイイ)ってなるかもしれない。
  • AngularのいいところがAngular以外で使えて嬉しい。
  • Schematicsの開発自体が結構楽しい。(ここ重要)
  • SchematicNameは複数定義できるので自分のネームスペースでnpmに公開おいて、プロジェクトに合わせて使ったり、今回はコンポーネント版でしたがRedux版やVue版も作りたいと思った。
  • チーム開発でもかなり強力に使えると思った。

おわり

ご静聴ありがとうございました!
icon


参考にさせていただきました!

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