【detekt / ktlint】元 Twitter 管理者 @mrmans0n らによって Jetpack Compose Rules フォークが進化している件

そもそも Twitter Jetpack Compose Rules が話題になったのは2、3年前、

その後の commits が過疎とは思っていましたが。

【detekt / ktlint】元 Twitter 管理者 @mrmans0n らによって Jetpack Compose Rules フォークが進化している件
👉 twitter/compose-rules: Static checks to aid with a healthy adoption of Compose hatena-bookmark

issues を見ていると以下のようなコメンツ。

Since both @mrmans0n and @chrisbanes left Twitter (sorry guys 😢), the future doesn't look so bright. At least not under Twitter's scope. Maybe this project can find a new home at Google, for instance?

👉 The future of this project · Issue #112 · twitter/compose-rules hatena-bookmark

I started my fork in https://github.com/mrmans0n/compose-rules

First order of business was supporting ktlint 0.48.x and kotlin 1.8.x. I submitted them as a PR in this repo too, to ease with transition, but I doubt anybody is looking on the Twitter side (all the admins of this repo don't work at the company any longer, and our permissions were removed).

I will work towards a maven central publication for my fork as soon as possible.

👉 The future of this project · Issue #112 · twitter/compose-rules hatena-bookmark

しれっと、フォークが元の管理者によって進化を続けています。

【detekt / ktlint】元 Twitter 管理者 @mrmans0n らによって Jetpack Compose Rules フォークが進化している件

Note
This repository is a fork of the Twitter Jetpack Compose Rules by its original maintainer. As none of the admins/maintainers continue working at the company, its development will continue here from now on. If you come from that project, check out the migration guide.

注意
このリポジトリは、元の管理者による Twitter Jetpack Compose Rules のフォークです。会社の管理者/保守者がもはや働いていないため、今後はこちらで開発が続行されます。もしあなたがそのプロジェクトから来た場合、移行ガイドをチェックしてください。

👉 mrmans0n/compose-rules: Static checks to aid with a healthy adoption of Compose. Maintained fork of the Twitter Compose rules. hatena-bookmark

使ってみると以下のような新しいルールで怒られる。


dependencies {
    detektPlugins "io.nlopez.compose.rules:detekt:<VERSION>"
}

The main Modifier of a @Composable should be applied once as a first modifier in the chain to the root-most layout in the component implementation.

You should move the modifier usage to the appropriate parent Composable.

See https://mrmans0n.github.io/compose-rules/rules/#modifiers-should-be-used-at-the-top-most-layout-of-the-component for more information. [ModifierNotUsedAtRoot]

@Composable のメインの Modifier は、コンポーネントの実装内の最もルートに配置されたレイアウトに対して最初の Modifier として一度だけ適用すべきです。Modifier の使用を適切な親の Composable に移動すべきです。

という感じで、Twitter オリジナル版では見つからなかった Modifier 使いまわし部分の違反が見つかります。

良さげです。

変更された rule をざっくり config.yml で確認する。

【detekt / ktlint】元 Twitter 管理者 @mrmans0n らによって Jetpack Compose Rules フォークが進化している件
👉 Comparing da89b71..027a205 · mrmans0n/compose-rules hatena-bookmark

👉 compose-rules/rules/detekt/src/main/resources/config/config.yml at main · mrmans0n/compose-rules hatena-bookmark
👉 compose-rules/rules/detekt/src/main/resources/config/config.yml at main · twitter/compose-rules hatena-bookmark

新しく追加されたルールを見てみます、

👉 compose-rules/docs/rules.md at main · mrmans0n/compose-rules hatena-bookmark

 

🧑‍💻 DefaultsVisibility - ComponentDefaults object should match the composable visibility

ComponentDefaults object should match the composable visibility
If your composable has an associated Defaults object to contain its default values, this object should have the same visibility as the composable itself. This will allow consumers to be able to interact or build upon the original intended defaults, as opposed to having to maintain their own set of defaults by copy-pasting.

ComponentDefaults オブジェクトはコンポーザブルの可視性に合わせるべきです。
コンポーザブルに関連付けられたデフォルト値を保持する Defaults オブジェクトがある場合、このオブジェクトはコンポーザブル自体と同じ可視性を持つべきです。これにより、コンシューマーは元々意図されたデフォルト値に対して対話したり、それを拡張したりすることができ、コピーアンドペーストで独自のデフォルト値セットを保守する必要がなくなります。

👉 compose-rules/docs/rules.md at main · mrmans0n/compose-rules hatena-bookmark

 

🧑‍💻 ModifierClickableOrder - Modifier order matters

Modifier order matters
The order of modifier functions is very important. Each function makes changes to the Modifierreturned by the previous function, the sequence affects the final result. Let's see an example of this:

修飾子の順序が重要です
修飾子関数の順序は非常に重要です。各関数は前の関数によって返された Modifier に変更を加え、その順序が最終的な結果に影響します。これについて例を見てみましょう:


@Composable
fun MyCard(modifier: Modifier = Modifier) {
    Column(
        modifier
            // Tapping on it does a ripple, the ripple is bound incorrectly to the composable
            .clickable { /* TODO */ }
            // Create rounded corners
            .clip(shape = RoundedCornerShape(8.dp))
            // Background with rounded corners
            .background(color = backgroundColor, shape = RoundedCornerShape(8.dp))
    ) {
        // rest of the implementation
    }
}

The entire area, including the clipped area and the clipped background, responds to clicks. This means that the ripple will fill it all, even the areas that we wanted to trim from the shape.

We can address this by simply reordering the modifiers.

切り抜かれた領域と切り抜かれた背景を含む、全体のエリアがクリックに反応します。つまり、リップルは、形状からトリミングしたい領域も含めて、全体を満たします。

この問題は、修飾子を単に再順序化することで解決できます。


@Composable
fun MyCard(modifier: Modifier = Modifier) {
    Column(
        modifier
            // Create rounded corners
            .clip(shape = RoundedCornerShape(8.dp))
            // Background with rounded corners
            .background(color = backgroundColor, shape = RoundedCornerShape(8.dp))
            // Tapping on it does a ripple, the ripple is bound incorrectly to the composable
            .clickable { /* TODO */ }
    ) {
        // rest of the implementation
    }
}

👉 compose-rules/docs/rules.md at main · mrmans0n/compose-rules hatena-bookmark

 

🧑‍💻 ModifierNaming - Naming modifiers properly

Naming modifiers properly
Composables that accept a Modifier as a parameter to be applied to the whole component represented by the composable function should name the parameter modifier.

In cases where Composables accept modifiers to be applied to a specific subcomponent should name the parameter xModifier (e.g. fooModifier for a Foo subcomponent) and follow the same guidelines above for default values and behavior.

適切な修飾子の命名
修飾子を受け入れるコンポーザブルは、そのコンポーザブル関数によって表されるコンポーネント全体に適用されるためのパラメータに modifier という名前を付けるべきです。

コンポーザブルが特定のサブコンポーネントに適用する修飾子を受け入れる場合、そのパラメータには xModifier(たとえば Foo サブコンポーネントの場合は fooModifier)という名前を付け、デフォルト値と動作に関して上記のガイドラインに従うべきです。

👉 compose-rules/docs/rules.md at main · mrmans0n/compose-rules hatena-bookmark

 

🧑‍💻 ModifierNotUsedAtRoot - Modifiers should be used at the top-most layout of the component

Modifiers should be used at the top-most layout of the component
Modifiers should be applied once as a first modifier in the chain to the root-most layout in the component implementation. Since modifiers aim to modify the external behaviors and appearance of the component, they must be applied to the top-most layout and be the first modifiers in the hierarchy. It is allowed to chain other modifiers to the modifier passed as a param if needed.

Modifier はコンポーネントの最上位レイアウトで使用するべきです
Modifier はコンポーネントの外部の動作との外観を変更することを目的としているため、最上位のレイアウトに適用され、階層の最初の Modifier に配置される必要があります。必要であれば、他の Modifier パラメーターとして、渡された Modifier に連鎖させることは許容されています。

👉 compose-rules/docs/rules.md at main · mrmans0n/compose-rules hatena-bookmark

 

🧑‍💻 PreviewAnnotationNaming - Naming multipreview annotations properly

Properly Naming Multipreview Annotations
Multipreview annotations should be named using "Previews" as a prefix. These annotations must be explicitly named to ensure they are easily distinguishable as an alternative to the @Preview when used.

適切な名前付け:マルチプレビューアノテーション
マルチプレビューアノテーションは、接頭辞として「Previews」を使用して命名すべきです。これらのアノテーションは、使用時に @Preview の代替として明確に識別できるように、明示的に名前をつける必要があります。

👉 compose-rules/docs/rules.md at main · mrmans0n/compose-rules hatena-bookmark

 

🧑‍💻 まとめ

どこまで管理が続くかは心配ですが、とりあえず今は良い感じです。

👉 【AndroidStudio】detekt で JetpackCompose 記述をチェックする hatena-bookmark
👉 【Gradle Plugin】detekt「baseline」とは hatena-bookmark