June 27, 2019
最近フロントエンドを開発する機会が多くて、その中でこれまであったほうがいいけどやる機会のなかった便利スクリプト的なもの触ったり、コマンドを用意する機会があったので、備忘録としてメモしておく。
ディレクトリの構成にもよるのだけど、現在開発しているプロジェクトではcomponent (UIのみを担うstatelessなcomponent)を1つのでディレクトリで管理している。 Reactのcomponentを開発するのに必要になるものは大体決まっていて、component本体(jsx)、css、storybook、testになる。これらを1つのディレクトリでまとめて管理する方法である。 大体1つのディレクトリの中身は以下のようになる。
- some-component
- index.tsx
- some-component.tsx
- som-component.css
- some-component.stories.tsx
(あとはテストとか)
これのメリットは2つあって、
である。
なので、scaffoldingをきちんとやらないと本来のメリットが受けられないのだが、こういうの開発してると後回しにしがちである。今回はこれを作成したのでメモとして残しておく。 何を使って実現してもいいのだと思うが、今回はこれをSchematicsを使って作成した。
SchematicsはAngular製のtemplateベースのscaffoldエンジンである。 Schematics自体の活用については以下のような良い記事が沢山すでにあるので説明はこれらの記事に任せることにする。
特徴として、
などがある。Angular製といえでも実際にはAngular以外でも活用することができるのでこれを採用した。
上記の記事を見ればほぼ分かるが、実際の利用例は以下の通り。
$ yarn build:schematics // 初回だけtranspile
$ yarn gen:component --path=./src/path/to/your/pages --name=component-name
と実行すると、以下のようなファイルが生成される。
src/path/to/your/pages
└── components
└── your-component
├── index.tsx
├── your-component.css
├── your-component.stories.tsx
└── your-component.tsx
それぞれのファイルの中身は以下の通り。
export { default, Props } from './your-component';
import React from 'react';
export interface Props {
}
const YourComponent: React.FC<Props> = () => {
return (
<div>
</div>
);
}
export default YourComponent;
import React from 'react';
import { storiesOf } from '@storybook/react';
import YourComponent, { Props } from './your-component';
const stories = storiesOf('components/YourComponent', module);
const props: Props = {
};
stories.add('YourComponent', () => <YourComponent {...props} />);
your-component.css
はファイルのみ
実際に作ったものはこれ。もともとSchematics自体は完全に独立した自分用のscaffoldエンジンとして作成できるが、今回はプロジェクトで使いたいものだったので、プロジェクトと独立したscaffoldではなく、プロジェクトのlib以下に配置し、npm scriptとして利用できるようにしている。
ディレクトリの構成は以下の通り。
lib/schematics
├── collection.json
├── src
│ └── component
│ ├── files
│ │ ├── __name@dasherize__.css
│ │ ├── __name@dasherize__.stories.tsx
│ │ ├── __name@dasherize__.tsx
│ │ └── index.tsx
│ └── index.ts
└── tsconfig.json
それぞれのファイルの中身は以下の通り。
import { strings } from '@angular-devkit/core';
import {
Rule,
SchematicsException,
apply,
branchAndMerge,
mergeWith,
template,
url,
move,
} from '@angular-devkit/schematics';
import { join } from 'path';
import { dasherize } from '@angular-devkit/core/src/utils/strings';
interface Options {
name: string;
path: string;
}
export default function(options: Options): Rule {
return () => {
const { name, path } = options;
if (!name) {
throw new SchematicsException('Option (name) is required.');
}
const fullPath = join(`${path}`, 'components', dasherize(name));
const templateSource = apply(url('./files'), [
template({
...strings,
...options,
}),
move(fullPath),
]);
return branchAndMerge(mergeWith(templateSource));
};
}
また、テンプレートとしては以下のようなファイルを用意する。
export { default, Props } from './<%= dasherize(name) %>';
import React from 'react';
export interface Props {
}
const <%= classify(name) %>: React.FC<Props> = () => {
return (
<div>
</div>
);
}
export default <%= classify(name) %>;
import React from 'react';
import { storiesOf } from '@storybook/react';
import <%= classify(name) %>, { Props } from './<%= dasherize(name) %>';
const stories = storiesOf('components/<%= classify(name) %>', module);
const props: Props = {
};
stories.add('<%= classify(name) %>', () => <<%= classify(name) %> {...props} />);
src/component/files/__name@dasherize__.css
は空にしてファイルだけおいている。
{
"$schema": "../../node_modules/@angular-devkit/schematics/collection-schema.json",
"schematics": {
"component": {
"description": "A component schematic for the project.",
"factory": "./built/component/index"
}
}
}
実際に使用するにはtsをtranspileしてから実行。以下のようなscriptを登録しておくと便利
"scripts": {
"build:schematics": "tsc -p ./lib/schematics && cp -r ./lib/schematics/src/component/files ./lib/schematics/built/component",
"gen:component": "schematics .:component --dry-run=false",
}
buildの方法がめっちゃ雑なのは一旦ご愛嬌で。。。
Reactのプロジェクトにおいて、Schematicsを用いたscaffoldingの方法を紹介した。 プロジェクトのメンテナビリティの担保と、DXの登録にはこういうscript用意しておくのが非常に大事。 これでファイルの置き場所に悩んだり、構成に悩むこともないので、管理しやすい。
ちょっと長くなってしまったので、残りは後編へ。SVGとw3colorのscriptについてメモを残しておく。
Personal blog by nobuhikosawai
who lives and works in Japan.