CSS

命名

クラス名の衝突を避けるために、クラス名は以下のガイドラインに従う必要があります。これらは、BEM (Block, Element, Modifier) メソッド に loosely inspired されています。

要素に割り当てられたすべてのクラス名は、パッケージの名前で始まり、その後にダッシュとコンポーネントが存在するディレクトリの名前が続きます。コンポーネントのルート要素のすべての子孫は、基本から2つの連続したアンダースコア `````````` で区切られたダッシュ区切りの記述子を追加する必要があります。

  • ルート要素: package-directory
  • 子要素: package-directory__descriptor-foo-bar

ルート要素は、index.js のデフォルトエクスポートによって返される最も高い祖先要素と見なされます。特に、フォルダーに複数のファイルが含まれている場合、それぞれが独自のデフォルトエクスポートされたコンポーネントを持っている場合、index.js の要素によってレンダリングされたものだけがルートと見なされます。他のすべては子孫として扱われるべきです。

例:

packages/components/src/notice/index.js にある次のコンポーネントを考えてみましょう:

  1. export default function Notice( { children, onRemove } ) {
  2. return (
  3. <div className="components-notice">
  4. <div className="components-notice__content">{ children }</div>
  5. <Button
  6. className="components-notice__dismiss"
  7. icon={ check }
  8. label={ __( 'Dismiss this notice' ) }
  9. onClick={ onRemove }
  10. />
  11. </div>
  12. );
  13. }

コンポーネントには、状態を示すクラス名を割り当てることができます(たとえば、「アクティブ」タブや「オープン」パネル)。これらの修飾子は、is- によって形容詞表現としてプレフィックスされる別のクラス名として適用されるべきです (is-active または is-opened)。稀に、可読性を向上させるために修飾子プレフィックスのバリエーションに遭遇することがあります (has-warning)。修飾子クラス名は特定のコンポーネントに文脈化されていないため、常に修正されるコンポーネントに伴ってスタイルシートに記述されるべきです (.components-panel.is-opened)。

例:

再度、Notices の例を考えてみましょう。私たちは、閉じることができる通知に特定のスタイリングを適用したいかもしれません。clsx パッケージ は、修飾子クラス名を条件付きで適用するための便利なユーティリティです。

  1. import clsx from 'clsx';
  2. export default function Notice( { children, onRemove, isDismissible } ) {
  3. const classes = clsx( 'components-notice', {
  4. 'is-dismissible': isDismissible,
  5. } );
  6. return <div className={ classes }>{ /* ... */ }</div>;
  7. }

コンポーネントのクラス名は、決して自分のフォルダーの外で使用されるべきではありません(_z-index.scss のような稀な例外を除く)。他のコンポーネントのスタイルを自分のコンポーネントに継承する必要がある場合は、その他のコンポーネントのインスタンスをレンダリングする必要があります。最悪の場合、スタイルを自分のコンポーネントのスタイルシート内に複製するべきです。これは、共有コンポーネントを再利用可能なインターフェースとして隔離することによってメンテナンス性を向上させ、限られたセットの共通コンポーネントを適応させてさまざまなユースケースをサポートすることを目的としています。

ブロックのための SCSS ファイル命名規則

ビルドプロセスは、Webpack が実行されると、ブロックライブラリディレクトリ内の SCSS を 2 つの別々の CSS ファイルに分割します。

style.scss ファイルに配置されたスタイルは、blocks/build/style.css にビルドされ、フロントエンドテーマおよびエディターで読み込まれます。エディターでのブロックの表示に特有の追加スタイルが必要な場合は、editor.scss に追加してください。

テーマとエディターの両方に表示されるスタイルの例には、ギャラリーの列やドロップキャップが含まれます。

JavaScript

Gutenberg の JavaScript は、ECMAScript 言語仕様 の最新の言語機能と、JSX 言語構文拡張 を使用しています。これらは、特にプロジェクトの Babel 設定でプリセットとして使用される @wordpress/babel-preset-default の組み合わせを通じて有効になります。

新しい JavaScript 言語機能を導入するための 段階的プロセス は、機能が完成と見なされる前に新機能を使用する機会を提供しますが、Gutenberg プロジェクトと @wordpress/babel-preset-default 設定は、ステージ 4(「完了」)に達した提案のサポートのみを対象とします。

インポート

Gutenberg プロジェクトでは、ES2015 インポート構文 を使用して、特定の機能のコード、異なる WordPress 機能間で共有されるコード、およびサードパーティの依存関係の間に明確な区分を持つモジュール式コードを作成します。

これらの区分は、他のファイルまたはソースからコードをインポートするファイルの先頭にある複数行のコメントによって識別されます。

外部依存関係

外部依存関係とは、WordPress の貢献者によって維持されていないサードパーティのコードであり、代わりに WordPress にデフォルトスクリプトとして含まれている か、npm のような外部パッケージマネージャーから参照されます。

例:

  1. /**
  2. * External dependencies
  3. */
  4. import moment from 'moment';

WordPress 依存関係

機能間の再利用性を促進するために、私たちの JavaScript は、export 1 つ以上の関数またはオブジェクトを持つドメイン固有のモジュールに分割されています。Gutenberg プロジェクトでは、これらのモジュールをトップレベルのディレクトリの下に区別しています。各モジュールは独立した目的を持ち、しばしばコードが共有されます。たとえば、テキストをローカライズするために、エディターコードは i18n モジュールから関数を含める必要があります。

例:

  1. /**
  2. * WordPress dependencies
  3. */
  4. import { __ } from '@wordpress/i18n';

内部依存関係

特定の機能内では、コードは別々のファイルとフォルダーに整理されています。外部および WordPress 依存関係と同様に、import キーワードを使用してこのコードをスコープに持ち込むことができます。ここでの主な違いは、内部ファイルをインポートする際には、作業しているトップレベルディレクトリに特有の相対パスを使用する必要があることです。

例:

  1. /**
  2. * Internal dependencies
  3. */
  4. import VisualEditor from '../visual-editor';

レガシー実験的 API、プラグイン専用 API、およびプライベート API

レガシー実験的 API

歴史的に、Gutenberg は experimental および unstable プレフィックスを使用して、特定の API がまだ安定しておらず、変更される可能性があることを示してきました。これは、プラグイン専用 API パターンまたは以下に説明するプライベート API パターンに代わるべきレガシーの慣習です。

プレフィックスを使用する問題は、これらの API が安定化または削除されることがほとんどなかったことです。2022 年 6 月の時点で、WordPress Core には、主要な WordPress リリース中に Gutenberg プラグインからマージされた 280 の公開エクスポートされた実験的 API が含まれていました。多くのプラグインやテーマは、他の方法ではアクセスできない重要な機能のためにこれらの実験的 API に依存し始めました。

レガシー experimental API は、もはや気まぐれに削除することはできません。これらは WordPress 公開 API の一部となり、WordPress 後方互換性ポリシー の対象となります。これらを削除するには、非推奨プロセスが必要です。いくつかの API では比較的簡単かもしれませんが、他の API では努力が必要で、複数の WordPress リリースにまたがる可能性があります。

全体として、新しい API に experimental プレフィックスを使用しないでください。代わりにプラグイン専用 API とプライベート API を使用してください。

プラグイン専用 API

プラグイン専用 API は、将来の改訂が保留中であるか、即時の手段を提供するモジュールからエクスポートされた一時的な値です。

外部消費者へ:

プラグイン専用 API に対するサポートのコミットメントはありません。 これらは、事前の警告なしに削除または変更される可能性があり、マイナーまたはパッチリリースの一部としても行われます。外部消費者として、これらの API を避けるべきです。

プロジェクト貢献者へ:

プラグイン専用 API は、最終的に公開される予定ですが、さらなる実験、テスト、および議論の対象です。できるだけ早く安定させるか、削除する必要があります。

プラグイン専用 API は WordPress Core から除外され、Gutenberg プラグインでのみ利用可能です:

  1. // Using globalThis.IS_GUTENBERG_PLUGIN allows Webpack to exclude this
  2. // export from WordPress core:
  3. if ( globalThis.IS_GUTENBERG_PLUGIN ) {
  4. export { doSomethingExciting } from './api';
  5. }

このような API の公開インターフェースはまだ最終決定されていません。コード内の参照を除いて、これらの API は文書化されるべきでも、CHANGELOG に言及されるべきでもありません。外部の観点からは、実質的に存在しないと見なされるべきです。ほとんどの場合、これらはこのリポジトリで維持されているパッケージ間の要件を満たすためにのみ公開されるべきです。

プラグイン専用 API は、一般に公開される API に安定化することが多いですが、それが保証されるわけではありません。

プライベート API

@wordpress パッケージは、プライベート API にプライベートにアクセスまたは公開することを希望する場合、

@wordpress/private-apis にオプトインすることによって行うことができます:

  1. // In packages/block-editor/private-apis.js:
  2. import { __dangerousOptInToUnstableAPIsOnlyForCoreModules } from '@wordpress/private-apis';
  3. export const { lock, unlock } =
  4. __dangerousOptInToUnstableAPIsOnlyForCoreModules(
  5. 'I acknowledge private features are not for use in themes or plugins and doing so will break in the next version of WordPress.',
  6. '@wordpress/block-editor' // Name of the package calling __dangerousOptInToUnstableAPIsOnlyForCoreModules,
  7. // (not the name of the package whose APIs you want to access)
  8. );

@wordpress パッケージは一度だけオプトインできます。このプロセスは、拡張者がそれを使用することを意図していないことを明確に伝えます。この文書は使用例に焦点を当てますが、@wordpress/private-apis パッケージの README.md で詳細を確認できます

パッケージがオプトインすると、lock() および unlock() ユーティリティを使用できます:

  1. // Say this object is exported from a package:
  2. export const publicObject = {};
  3. // However, this string is internal and should not be publicly available:
  4. const privateString = 'private information';
  5. // Solution: lock the string "inside" of the object:
  6. lock( publicObject, privateString );
  7. // The string is not nested in the object and cannot be extracted from it:
  8. console.log( publicObject );
  9. // {}
  10. // The only way to access the string is by "unlocking" the object:
  11. console.log( unlock( publicObject ) );
  12. // "private information"
  13. // lock() accepts all data types, not just strings:
  14. export const anotherObject = {};
  15. lock( anotherObject, function privateFn() {} );
  16. console.log( unlock( anotherObject ) );
  17. // function privateFn() {}

lock() および unlock() を使用して、異なる種類の private API を公開エクスポートしないようにする方法を学び続けてください。

プライベートセレクターとアクション

プライベートセレクターとアクションを公開ストアにアタッチできます:

  1. // In packages/package1/store.js:
  2. import { privateHasContentRoleAttribute } from './private-selectors';
  3. import { privateToggleFeature } from './private-actions';
  4. // The `lock` function is exported from the internal private-apis.js file where
  5. // the opt-in function was called.
  6. import { lock, unlock } from './lock-unlock';
  7. export const store = registerStore( /* ... */ );
  8. // Attach a private action to the exported store:
  9. unlock( store ).registerPrivateActions( {
  10. privateToggleFeature,
  11. } );
  12. // Attach a private action to the exported store:
  13. unlock( store ).registerPrivateSelectors( {
  14. privateHasContentRoleAttribute,
  15. } );
  1. // In packages/package2/MyComponent.js:
  2. import { store } from '@wordpress/package1';
  3. import { useSelect } from '@wordpress/data';
  4. // The `unlock` function is exported from the internal private-apis.js file where
  5. // the opt-in function was called.
  6. import { unlock } from './lock-unlock';
  7. function MyComponent() {
  8. const hasRole = useSelect(
  9. ( select ) =>
  10. // Use the private selector:
  11. unlock( select( store ) ).privateHasContentRoleAttribute()
  12. // Note the unlock() is required. This line wouldn't work:
  13. // select( store ).privateHasContentRoleAttribute()
  14. );
  15. // Use the private action:
  16. unlock( useDispatch( store ) ).privateToggleFeature();
  17. // ...
  18. }
プライベート関数、クラス、および変数
  1. // In packages/package1/index.js:
  2. import { lock } from './lock-unlock';
  3. export const privateApis = {};
  4. /* Attach private data to the exported object */
  5. lock( privateApis, {
  6. privateCallback: function () {},
  7. privateReactComponent: function PrivateComponent() {
  8. return <div />;
  9. },
  10. privateClass: class PrivateClass {},
  11. privateVariable: 5,
  12. } );
  1. // In packages/package2/index.js:
  2. import { privateApis } from '@wordpress/package1';
  3. import { unlock } from './lock-unlock';
  4. const {
  5. privateCallback,
  6. privateReactComponent,
  7. privateClass,
  8. privateVariable,
  9. } = unlock( privateApis );

プライベートアクションとセレクターは、登録されたストアで常に登録することを忘れないでください。

時にはそれが簡単です:

  1. export const store = createReduxStore( STORE_NAME, storeConfig() );
  2. // `register` uses the same `store` object created from `createReduxStore`.
  3. register( store );
  4. unlock( store ).registerPrivateActions( {
  5. // ...
  6. } );

ただし、いくつかのパッケージは createReduxStore registerStore の両方を呼び出す場合があります。この場合、常に登録されたストアを選択してください:

  1. export const store = createReduxStore( STORE_NAME, {
  2. ...storeConfig,
  3. persist: [ 'preferences' ],
  4. } );
  5. const registeredStore = registerStore( STORE_NAME, {
  6. ...storeConfig,
  7. persist: [ 'preferences' ],
  8. } );
  9. unlock( registeredStore ).registerPrivateActions( {
  10. // ...
  11. } );

プライベート関数引数

安定した関数にプライベート引数を追加するには、

その関数の安定したバージョンとプライベートバージョンを準備する必要があります。

次に、安定した関数をエクスポートし、lock() 不安定な関数をその中に

  1. // In @wordpress/package1/index.js:
  2. import { lock } from './lock-unlock';
  3. // A private function contains all the logic
  4. function privateValidateBlocks( formula, privateIsStrict ) {
  5. let isValid = false;
  6. // ...complex logic we don't want to duplicate...
  7. if ( privateIsStrict ) {
  8. // ...
  9. }
  10. // ...complex logic we don't want to duplicate...
  11. return isValid;
  12. }
  13. // The stable public function is a thin wrapper that calls the
  14. // private function with the private features disabled
  15. export function validateBlocks( blocks ) {
  16. privateValidateBlocks( blocks, false );
  17. }
  18. export const privateApis = {};
  19. lock( privateApis, { privateValidateBlocks } );
  1. // In @wordpress/package2/index.js:
  2. import { privateApis as package1PrivateApis } from '@wordpress/package1';
  3. import { unlock } from './lock-unlock';
  4. // The private function may be "unlocked" given the stable function:
  5. const { privateValidateBlocks } = unlock( package1PrivateApis );
  6. privateValidateBlocks( blocks, true );

プライベート React コンポーネントプロパティ

安定したコンポーネントにプライベート引数を追加するには、

そのコンポーネントの安定したバージョンとプライベートバージョンを準備する必要があります。

次に、安定した関数をエクスポートし、lock() 不安定な関数をその中に

  1. // In @wordpress/package1/index.js:
  2. import { lock } from './lock-unlock';
  3. // The private component contains all the logic
  4. const PrivateMyButton = ( { title, privateShowIcon = true } ) => {
  5. // ...complex logic we don't want to duplicate...
  6. return (
  7. <button>
  8. { privateShowIcon && <Icon src={ someIcon } /> } { title }
  9. </button>
  10. );
  11. };
  12. // The stable public component is a thin wrapper that calls the
  13. // private component with the private features disabled
  14. export const MyButton = ( { title } ) => (
  15. <PrivateMyButton title={ title } privateShowIcon={ false } />
  16. );
  17. export const privateApis = {};
  18. lock( privateApis, { PrivateMyButton } );
  1. // In @wordpress/package2/index.js:
  2. import { privateApis } from '@wordpress/package1';
  3. import { unlock } from './lock-unlock';
  4. // The private component may be "unlocked" given the stable component:
  5. const { PrivateMyButton } = unlock( privateApis );
  6. export function MyComponent() {
  7. return <PrivateMyButton data={ data } privateShowIcon={ true } />;
  8. }

プライベートエディタ設定

WordPress 拡張者は、自分自身でプライベートブロック設定を更新することはできません。updateSettings() ストアのアクションは、公開 API の一部ではないすべての設定をフィルタリングします。実際にそれらを保存する唯一の方法は、プライベートアクション experimentalUpdateSettings() を介してです。

ブロックエディタ設定をプライベート化するには、/packages/block-editor/src/store/actions.jsprivateSettings リストに追加します:

  1. const privateSettings = [
  2. 'inserterMediaCategories',
  3. // List a block editor setting here to make it private
  4. ];

プライベート block.json および theme.json API

今日の時点で、block.json および theme.json API を Gutenberg コードベースに制限する方法はありません。将来的には、新しいプライベート API は、コア WordPress ブロックにのみ適用され、プラグインやテーマはそれにアクセスできなくなります。

サンクのインライン小アクション

最後に、新しいアクションクリエイターを導入する代わりに、サンク:

  1. export function toggleFeature( scope, featureName ) {
  2. return function ( { dispatch } ) {
  3. dispatch( { type: '__private_BEFORE_TOGGLE' } );
  4. // ...
  5. };
  6. }

プライベート API を公開する

一部のプライベート API はコミュニティのフィードバックから利益を得る可能性があり、WordPress 拡張者に公開することは理にかなっています。同時に、これらを WordPress コアの公開 API に変えることは理にかなっていません。どうすればよいですか?

そのプライベート API をプラグイン専用 API として再エクスポートし、Gutenberg プラグインでのみ公開することができます:

  1. // This function can't be used by extenders in any context:
  2. function privateEverywhere() {}
  3. // This function can be used by extenders with the Gutenberg plugin but not in vanilla WordPress Core:
  4. function privateInCorePublicInPlugin() {}
  5. // Gutenberg treats both functions as private APIs internally:
  6. const privateApis = {};
  7. lock( privateApis, { privateEverywhere, privateInCorePublicInPlugin } );
  8. // The privateInCorePublicInPlugin function is explicitly exported,
  9. // but this export will not be merged into WordPress core thanks to
  10. // the globalThis.IS_GUTENBERG_PLUGIN check.
  11. if ( globalThis.IS_GUTENBERG_PLUGIN ) {
  12. export const privateInCorePublicInPlugin =
  13. unlock( privateApis ).privateInCorePublicInPlugin;
  14. }

オブジェクト

可能な場合は、オブジェクトプロパティ値を定義する際に 省略記法 を使用してください:

  1. const a = 10;
  2. // Bad:
  3. const object = {
  4. a: a,
  5. performAction: function () {
  6. // ...
  7. },
  8. };
  9. // Good:
  10. const object = {
  11. a,
  12. performAction() {
  13. // ...
  14. },
  15. };

文字列

文字列リテラルは、単一引用符で宣言する必要があります。ただし、文字列自体にエスケープする必要がある単一引用符が含まれている場合は、その場合はダブルクォートを使用します。文字列に単一引用符 ダブルクォートが含まれている場合、エスケープを避けるために ES6 テンプレート文字列を使用できます。

注意: 単一引用符文字 (') は、 のような単語に対してアポストロフィの代わりに使用されるべきではありません。テストコードでは、実際のアポストロフィを使用することが推奨されます。

一般的に、バックスラッシュで引用符をエスケープすることは避けてください:

  1. // Bad:
  2. const name = "Matt";
  3. // Good:
  4. const name = 'Matt';
  5. // Bad:
  6. const pet = "Matt's dog";
  7. // Also bad (not using an apostrophe):
  8. const pet = "Matt's dog";
  9. // Good:
  10. const pet = 'Matt’s dog';
  11. // Also good:
  12. const oddString = "She said 'This is odd.'";

可能な限り、文字列連結の代わりに ES6 テンプレート文字列を使用してください:

  1. const name = 'Stacey';
  2. // Bad:
  3. alert( 'My name is ' + name + '.' );
  4. // Good:
  5. alert( `My name is ${ name }.` );

オプショナルチェイニング

オプショナルチェイニング は、ECMAScript 仕様の 2020 年版で導入された新しい言語機能です。この機能は、潜在的に null-ish なオブジェクトのプロパティアクセスに非常に便利ですが、オプショナルチェイニングを使用する際に注意すべき一般的な落とし穴がいくつかあります。これらは、将来的にリントや型チェックが保護するのに役立つ問題かもしれません。その間、以下の項目に注意する必要があります:

  • オプショナルチェイニングで評価された値の結果を否定する場合 (!)、オプショナルチェイニングが進行できないポイントに達した場合、falsy 値 が生成され、否定されたときに true に変換されることに注意してください。多くの場合、これは予期しない結果です。
    • 例: const hasFocus = ! nodeRef.current?.contains( document.activeElement ); は、nodeRef.current が割り当てられていない場合、true を返します。
    • 関連する問題を参照: #21984
    • 類似の ESLint ルールを参照: no-unsafe-negation
  • ブール値を割り当てる場合、オプショナルチェイニングは falsy (undefined, null) を生成する可能性がありますが、厳密には false ではありません。これは、値がブール値であることが期待される方法で渡されるときに問題になる可能性があります (true または false)。ブール値は、論理が真偽を広く考慮する方法で使用されることが多いため、これらの問題は他のオプショナルチェイニングでも発生する可能性があります。型チェックは、これらのエラーを防ぐのに役立つかもしれません。
    • 例: document.body.classList.toggle( 'has-focus', nodeRef.current?.contains( document.activeElement ) ); は、2 番目の引数がオプションであるため、クラスを誤って追加する可能性があります。undefined が渡されると、false が渡されたときのようにクラスを解除しません。
    • 例: <input value={ state.selected?.value.trim() } /> は、制御された入力と制御されていない入力 の間で切り替えることによって、React で警告を引き起こす可能性があります。これは、trim() の結果が常に文字列値を返すと早合点する際に陥りやすい罠であり、オプショナルチェイニングが早期に評価を中止し、undefined の値を返す可能性があることを見落としています。

React コンポーネント

すべてのコンポーネントは、関数コンポーネント として実装することが推奨されており、フック を使用してコンポーネントの状態とライフサイクルを管理します。エラーバウンダリー を除いて、クラスコンポーネントを使用しなければならない状況に遭遇することはありません。ここで注意すべきは、WordPress のコードリファクタリングに関するガイダンス が適用されることです: クラスコンポーネントを一括で更新するための集中した努力は必要ありません。代わりに、他の変更と組み合わせて良いリファクタリングの機会と考えてください。

JSDoc を使用した JavaScript ドキュメント

Gutenberg は、WordPress JavaScript ドキュメント標準 に従い、ファイルの整理における インポートセマンティクス の独自の使用、型検証のための [TypeScript ツール] (a28c3946e7fb1861.md#javascript-testing) の使用、および @wordpress/docgen を使用した自動ドキュメント生成に関する追加ガイドラインを持っています。

追加のガイダンスについては、以下のリソースを参照してください:

カスタムタイプ

JSDoc @typedef タグ を使用してカスタムタイプを定義します。

カスタムタイプには説明を含め、常にその基本タイプを含める必要があります。

カスタムタイプは、意味の明確さを保ちながら、できるだけ簡潔に命名する必要があります。他のグローバルまたはスコープされたタイプとの衝突を避けるために、WP プレフィックスをすべてのカスタムタイプに適用する必要があります。冗長なプレフィックスやサフィックス(たとえば、Type サフィックスや Custom プレフィックス)を避けてください。カスタムタイプはデフォルトでグローバルではないため、特定のパッケージに対して過度に特異である必要はありません。ただし、他のタイプと同じスコープに持ち込まれたときに曖昧さや名前の衝突を避けるために、十分な特異性を持って命名する必要があります。

  1. /**
  2. * A block selection object.
  3. *
  4. * @typedef WPBlockSelection
  5. *
  6. * @property {string} clientId A block client ID.
  7. * @property {string} attributeKey A block attribute key.
  8. * @property {number} offset An attribute value offset, based on the rich
  9. * text value.
  10. */

@typedef とタイプ名の間に {Object} はありません。以下の @property はオブジェクトのためのタイプであることを示しているため、オブジェクトのタイプを定義したい場合は {Object} を使用しないことをお勧めします。

カスタムタイプは、事前定義されたオプションのセットを説明するためにも使用できます。型のユニオン は、リテラル値をインラインタイプとして使用することができますが、80 文字の最大行長を尊重しながらタグを整列させるのは難しい場合があります。ユニオンタイプを定義するためにカスタムタイプを使用することで、これらのオプションの目的を説明する機会が得られ、行の長さの問題を回避するのに役立ちます。

  1. /**
  2. * Named breakpoint sizes.
  3. *
  4. * @typedef {'huge'|'wide'|'large'|'medium'|'small'|'mobile'} WPBreakpoint
  5. */

文字列リテラルのセットを定義する際の引用符の使用に注意してください。JavaScript コーディング標準 に従い、文字列リテラルをタイプまたは デフォルト関数パラメーター として割り当てる場合、単一引用符を使用する必要があります。また、インポートされたタイプの パスを指定する 場合にも単一引用符を使用する必要があります。

タイプのインポートとエクスポート

TypeScript import 関数 を使用して、他のファイルやサードパーティの依存関係から型宣言をインポートします。

インポートされた型宣言は、利用可能な行長を超えて冗長になる可能性があるため、複数回参照される場合は、ファイルの先頭で @typedef 宣言を使用して外部型のエイリアスを作成することをお勧めします。

  1. /** @typedef {import('@wordpress/data').WPDataRegistry} WPDataRegistry */

他のファイルで定義されたすべてのカスタムタイプをインポートできます。

WordPress パッケージからどのタイプを利用可能にするかを考慮する際、パッケージのエントリポイントスクリプトの @typedef ステートメントは、その公開 API と実質的に同じものとして扱われるべきです。これを認識することは重要であり、公開インターフェースで内部タイプを意図せずに公開しないようにし、プロジェクトの公開タイプを公開する方法としても重要です。

  1. // packages/data/src/index.js
  2. /** @typedef {import('./registry').WPDataRegistry} WPDataRegistry */

このスニペットでは、@typedef が前の例の import('@wordpress/data') の使用をサポートします。

外部依存関係

多くのサードパーティの依存関係は、独自の TypeScript タイピングを配布します。これらについては、import セマンティクスは「そのまま機能する」必要があります。

作業例: `````import````` タイプ

エディター用の TypeScript 統合 を使用している場合、型がフォールバック any 型以外のもので解決される場合、通常はこれが機能することがわかります。

独自の TypeScript タイプを配布しないパッケージについては、DefinitelyTyped コミュニティが維持する型定義をインストールして使用することができます。

ジェネリックタイプ

ジェネリックタイプを文書化する際は、ObjectFunctionPromise など、期待されるレコードタイプに関する詳細を常に含めてください。

  1. // Bad:
  2. /** @type {Object} */
  3. /** @type {Function} */
  4. /** @type {Promise} */
  5. // Good:
  6. /** @type {Record<string,number>} */ /* or */ /** @type {{[setting:string]:any}} */
  7. /** @type {(key:string)=>boolean} */
  8. /** @type {Promise<string>} */

オブジェクトが辞書として使用される場合、そのタイプを 2 つの方法で定義できます: インデックス可能なインターフェース ({[setting:string]:any}) または Record。オブジェクトのキーの名前が setting のように開発者に何をすべきかを示す場合は、インデックス可能なインターフェースを使用します。そうでない場合は、Record を使用します。

ここでの関数式は、期待されるパラメーターの名前と型に関する詳細情報を提供するのに役立つ TypeScript の構文を使用しています。詳細については、TypeScript @type タグ関数の推奨事項 を参照してください。

より高度なケースでは、TypeScript @template タグ を使用して、ジェネリックタイプとして独自のカスタムタイプを定義できます。

「カスタムタイプ」に関するアドバイスと同様に、リテラル値を持つ型のユニオンについては、オブジェクトレコードの期待されるキー値をよりよく説明するためにカスタムタイプ @typedef を作成することを検討できます。

  1. /**
  2. * An apiFetch middleware handler. Passed the fetch options, the middleware is
  3. * expected to call the `next` middleware once it has completed its handling.
  4. *
  5. * @typedef {(options:WPAPIFetchOptions,next:WPAPIFetchMiddleware)=>void} WPAPIFetchMiddleware
  6. */
  1. /**
  2. * Named breakpoint sizes.
  3. *
  4. * @typedef {"huge"|"wide"|"large"|"medium"|"small"|"mobile"} WPBreakpoint
  5. */
  6. /**
  7. * Hash of breakpoint names with pixel width at which it becomes effective.
  8. *
  9. * @type {Record<WPBreakpoint,number>}
  10. */
  11. const BREAKPOINTS = { huge: 1440 /* , ... */ };

ヌラブル、未定義、および void タイプ

先頭に ? を付けてヌラブルタイプを表現できます。ヌラブル形式の型は、型または明示的な null 値を説明する場合にのみ使用してください。オプションのパラメーターの指標としてヌラブル形式を使用しないでください。

  1. /**
  2. * Returns a configuration value for a given key, if exists. Returns null if
  3. * there is no configured value.
  4. *
  5. * @param {string} key Configuration key to retrieve.
  6. *
  7. * @return {?*} Configuration value, if exists.
  8. */
  9. function getConfigurationValue( key ) {
  10. return config.hasOwnProperty( key ) ? config[ key ] : null;
  11. }

同様に、undefined 型は、undefined の明示的な値を期待する場合にのみ使用してください。

  1. /**
  2. * Returns true if the next HTML token closes the current token.
  3. *
  4. * @param {WPHTMLToken} currentToken Current token to compare with.
  5. * @param {WPHTMLToken|undefined} nextToken Next token to compare against.
  6. *
  7. * @return {boolean} True if `nextToken` closes `currentToken`, false otherwise.
  8. */

パラメーターがオプションの場合は、角括弧表記 を使用します。オプションのパラメーターにデフォルト値があり、関数式で デフォルトパラメーター として表現できる場合、JSDoc にその値を含める必要はありません。関数パラメーターに複雑なロジックが必要な実効的なデフォルト値がある場合は、JSDoc にデフォルト値を含めることを選択できます。

  1. /**
  2. * Renders a toolbar.
  3. *
  4. * @param {Object} props Component props.
  5. * @param {string} [props.className] Class to set on the container `<div />`.
  6. */

関数に return ステートメントが含まれていない場合、その関数は void 戻り値を持つと言われます。戻り値の型が void の場合、@return タグを含める必要はありません。

関数に複数のコードパスがあり、一部(ただしすべてではない)条件が return ステートメントを生成する場合、void 型を含むユニオン型としてこれを文書化できます。

  1. /**
  2. * Returns a configuration value for a given key, if exists.
  3. *
  4. * @param {string} key Configuration key to retrieve.
  5. *
  6. * @return {*|void} Configuration value, if exists.
  7. */
  8. function getConfigurationValue( key ) {
  9. if ( config.hasOwnProperty( key ) ) {
  10. return config[ key ];
  11. }
  12. }

関数型 を文書化する際は、void 戻り値の型を常に含める必要があります。そうしないと、関数は混合(「任意」)値を返すと推測され、void 結果ではなくなります。

  1. /**
  2. * An apiFetch middleware handler. Passed the fetch options, the middleware is
  3. * expected to call the `next` middleware once it has completed its handling.
  4. *
  5. * @typedef {(options:WPAPIFetchOptions,next:WPAPIFetchMiddleware)=>void} WPAPIFetchMiddleware
  6. */

例の文書化

@wordpress/docgen ツールを使用して生成されたドキュメントには、@example タグが定義されている場合、含まれるため、関数やコンポーネントの使用例を含めることはベストプラクティスと見なされます。これは、パッケージの公開 API の文書化されたメンバーにとって特に重要です。

例を文書化する際は、コードサンプルの開始と終了を示すためにマークダウン \`\`\` コードブロックを使用してください。例は複数行にわたることができます。

  1. /**
  2. * Given the name of a registered store, returns an object of the store's
  3. * selectors. The selector functions are been pre-bound to pass the current
  4. * state automatically. As a consumer, you need only pass arguments of the
  5. * selector, if applicable.
  6. *
  7. * @param {string} name Store name.
  8. *
  9. * @example
  10. * ```js
  11. * select( 'my-shop' ).getPrice( 'hammer' );
  12. * ```
  13. *
  14. * @return {Record<string,WPDataSelector>} Object containing the store's
  15. * selectors.
  16. */

React コンポーネントの文書化

可能な場合は、すべてのコンポーネントを 関数コンポーネント として実装し、フック を使用してコンポーネントのライフサイクルと状態を管理します。

関数コンポーネントの文書化は、他の関数と同じように扱うべきです。コンポーネントを文書化する際の主な注意点は、関数が通常、単一の引数(「props」)のみを受け入れることを認識することです。この引数には多くのプロパティメンバーが含まれる場合があります。個々の prop タイプを文書化するには、パラメータプロパティのドット構文 を使用してください。

  1. /**
  2. * Renders the block's configured title as a string, or empty if the title
  3. * cannot be determined.
  4. *
  5. * @example
  6. *
  7. * ```jsx
  8. * <BlockTitle clientId="afd1cb17-2c08-4e7a-91be-007ba7ddc3a1" />
  9. * ```
  10. *
  11. * @param {Object} props
  12. * @param {string} props.clientId Client ID of block.
  13. *
  14. * @return {?string} Block title.
  15. */

クラスコンポーネントの場合、コンポーネントの props を文書化するための推奨事項はありません。Gutenberg は propTypes 静的クラスメンバー を使用または推奨していません。

PHP

私たちは

phpcs (PHP_CodeSniffer) を使用して、WordPress コーディング基準ルールセット に対してこのプロジェクトのすべての PHP コードに対して多くの自動チェックを実行します。これにより、WordPress PHP コーディング基準に一貫性を持たせることができます。

PHPCS を使用する最も簡単な方法は、ローカル環境 です。それがインストールされると、npm run lint:php を実行して PHP をチェックできます。

PHPCS をローカルにインストールすることを好む場合は、composer を使用する必要があります。composer をインストール してから、composer install を実行します。これにより、phpcsWordPress-Coding-Standards がインストールされ、composer lint を介して実行できるようになります。