emotion v10 になって変わったこと
CSS-in-JS ライブラリの emotion が v10 から、だいぶ使い方が変わっていました。
package の変更
v9 では emotion
package を import していました。
import { css } from "emotion"
v10 では React を使っている場合は @emotion/core
を、それ以外の場合は emotion
を import するようになっています。
(後述しますが React も emotion
package を利用することも可能です)
https://emotion.sh/docs/install
React の場合
/** @jsx jsx */ import { jsx, css } from "@emotion/core" const style = css` color: red; `; function Foo() { return ( <div css={style}> <p>Foo Component</p> </div> ); }
それ以外の場合
import { css } from "emotion" const style = css` color: red; `; const foo = document.querySelector(".foo"); foo.classList.add(style);
@emotion/core について
emotion は css
で定義した style を class
名に指定することで css を適用可能で、利用しているライブラリ(React や Vue)への依存を少なくできる点が特徴的でした。
しかし v10 からは React の場合 @emotion/core
という別 package を利用することになってしまいました。
この理由ですが、Server Side Rendering (SSR) を行う際の手間を省略するためのようです。
React では @emotion/core
から jsx
と css
の2つを import したうえで /** @jsx jsx */
という JSX Pragma を記載します。
/** @jsx jsx */ import { jsx, css } from "@emotion/core"
この JSX Pragma は、JSX をコンパイルする際に利用する関数を変更するマジックコメントです。
初期値は React.createElement
になっています。
- https://emotion.sh/docs/css-prop#jsx-pragma
- https://babeljs.io/docs/en/babel-plugin-transform-react-jsx#pragma
emotion v10 では、JSX の処理を自前の jsx
で行うことで css
prop に指定された style を処理しています。
React.ceateElement
の差し替えは少し不安になりますが、これによって Server Side Rendering を行う際に、style を別で処理する必要を無くしたようです。
import { renderToString } from 'react-dom/server'; import App from './App' let html = renderToString(<App />)
https://emotion.sh/docs/ssr より引用
一方で、vanilla Emotion を利用した場合の Server Side Rendering の方法も記載されています。
import { renderStylesToString } from 'emotion-server' import { cache } from 'emotion' import { CacheProvider } from '@emotion/core' import { renderToString } from 'react-dom/server' let element = ( <CacheProvider value={cache}> <App /> </CacheProvider> ) let html = renderStylesToString(renderToString(element))
https://emotion.sh/docs/ssr より引用
ということで、emotion v10 で React でも引き続き emotion
package の css
で作成した style を className
prop に指定する方法が使えるみたいです。
まとめ
- emotion v10 から用途に合わせて package が分割
- React では
@emotion/core
oremotion
- それ以外では
emotion
- React では
@emotion/core
では JSX Pragma を利用してcss
prop の処理を JSX と一緒に行う- Server Side Rendering を行う際に style の処理を気にしなくて良い
SAM CLI で AWS SAM と Swagger を用いた API 開発環境を構築する
追記 (2018-10-02)
!FindInMap
を AWS::Include
とともに用いると aws cloudformation deploy
で失敗することが分かったため、 SwaggerPath
をパラメータとして受け取る形に修正しました。
AWS SAM と Swagger を用いてサーバーレスで API を実装する際の開発環境を SAM CLI で構築しました。
AWS SAM と SAM CLI について
AWS SAM(AWS サーバーレスアプリケーションモデル)は API Gateway、Lambda、DynamoDB を管理することが出来る CloudFormation の拡張です。
また SAM CLI (aws-sam-cli) は AWS SAM のテンプレートをもとに、ローカルでの開発やデプロイする環境を提供してくれるツールです。
AWS SAM と Swagger
AWS SAM では API の定義に Swagger 形式を用いることが可能です。
上記の記事にある通り Swagger を用いる方法として、以下の3種類の方法があります。
今回は、AWS SAM のテンプレートファイルから AWS:Include
で swagger.yml を読み込む方法のアプリケーションの開発環境を SAM CLI で構築します。
ローカル開発環境の構築
まずは SAM CLI をインストールします。
SAM CLI は Python 製のため pip
を使います。
$ pip install aws-sam-cli
続いて、サンプルリポジトリを git clone
します。
$ git clone https://github.com/yami-beta/aws-sam-swagger-sample.git
template.yml
が AWS SAM のテンプレートで swagger.yml
が Swagger Spec になります。
API を起動する場合は以下のコマンドを実行します。
$ sam local start-api
開発環境構築のポイント
開発環境を構築する上でのポイントが以下の部分です。
DefinitionBody
での swagger.yml の指定に SwaggerPath
というパラメータを用いています。
開発環境( Stage
パラメータが dev
)の場合はローカルのパスを、ステージング環境( Stage
パラメータが stg
)の場合は S3 バケットとなるように SwaggerPath
を指定します。
Parameters: SwaggerPath: Type: String Default: swagger.yml Resources: HelloApi: Type: AWS::Serverless::Api Properties: StageName: !Ref Stage DefinitionBody: Fn::Transform: Name: AWS::Include Parameters: Location: !Ref SwaggerPath
以下の Mappings
の例はデプロイできない古い情報です。
DefinitionBody
で swagger.yml を指定しているのですが、開発環境( Stage
パラメータが dev
)の場合はローカルのパスを、ステージング環境( Stage
パラメータが stg
)の場合は S3 バケットとなるように、Mappings を用いています。
Mappings: StageConf: dev: ArtifactBucket: swagger.yml stg: ArtifactBucket: !Sub s3://${ArtifactBucket}/swagger.yml Resources: HelloApi: Type: AWS::serverless::Api Properties: StageName: !Ref Stage DefinitionBody: Fn::Transform: Name: AWS::Include Parameters: Location: !FindInMap [StageConf, !Ref Stage, "ArtifactBucket"]
AWS:Include
はローカルのファイルを指定することが現時点では出来ません。
しかし SAM CLI ではローカルのパスを指定すると読み込んでくれます。
これを利用して、開発環境ではローカルの swagger.yml を用いて sam local start-api
を実行し、デプロイ時には S3 バケット名とすることで、開発環境と実際に AWS にデプロイする場合で共通のテンプレートを利用できます。
def _download_swagger(self, location): """ Download the file from given local or remote location and return it Parameters ---------- location : str or dict Local path or S3 path to Swagger file to download. Consult the ``__init__.py`` documentation for specifics on structure of this property. Returns ------- dict or None Downloaded and parsed Swagger document. None, if unable to download """ if not location: return bucket, key, version = self._parse_s3_location(location) if bucket and key: LOG.debug("Downloading Swagger document from Bucket=%s, Key=%s, Version=%s", bucket, key, version) swagger_str = self._download_from_s3(bucket, key, version) return yaml_parse(swagger_str) if not isinstance(location, string_types): # This is not a string and not a S3 Location dictionary. Probably something invalid LOG.debug("Unable to download Swagger file. Invalid location: %s", location) return # ``location`` is a string and not a S3 path. It is probably a local path. Let's resolve relative path if any filepath = location if self.working_dir: # Resolve relative paths, if any, with respect to working directory filepath = os.path.join(self.working_dir, location) if not os.path.exists(filepath): LOG.debug("Unable to download Swagger file. File not found at location %s", filepath) return LOG.debug("Reading Swagger document from local file at %s", filepath) with open(filepath, "r") as fp: return yaml_parse(fp.read())
aws-sam-cli/reader.py at 6164d6d2e7351a849ad3d79973ac18b8d3d1d371 · awslabs/aws-sam-cli · GitHub
まとめ
SAM CLI を用いて AWS SAM と Swagger で構成された API の開発環境を構築しました。
現在は AWS:Include
がローカルパスを指定できないため、Mappings パラメータ でローカルパスと S3 バケットを切り替えることで、テンプレートを共通化することができます。
AWS:Include
がローカルパスを指定できるようになると、更にシンプルなテンプレートに出来るため、今後に期待したいところです。