UNPKG

chrome-devtools-frontend

Version:
291 lines (225 loc) 13.8 kB
--- name: devtools-setting-migration description: "Workflow for splitting an existing SettingRegistration into a SettingDescriptor (placed in the lowest layer where used: core/, models/, or ui/settings/) and SettingUIDescriptor (registered in a higher-level -meta.ts file, outside of core/ and models/)." --- # DevTools Setting Migration Guide This skill guide describes how to migrate an existing legacy `SettingRegistration` in DevTools by splitting it into a `SettingDescriptor` (non-UI descriptor) and a `SettingUIDescriptor` (UI descriptor). --- ## Key Principles & Layer Boundaries ### 1. `SettingDescriptor` Location ("Lowest Layer") * **Lowest Layer Rule:** A `SettingDescriptor` MUST be placed in the lowest architectural layer where the setting is used. * **Core / Model Settings:** If a setting is used by `core/sdk` (or `models/`), its `SettingDescriptor` should be located in `core/sdk` (or `models/`). * **Panel / UI Settings:** If a setting is only used by UI components or a specific panel (e.g. `panels/console/`), its `SettingDescriptor` **MUST GO INTO `ui/settings/FooSettings.ts`** (e.g. `ui/settings/ConsoleSettings.ts` where `Foo` is the panel name). * **CRITICAL RULE — PANEL DESCRIPTORS MUST NOT BE PLACED IN `panels/`:** * A `SettingDescriptor` **MUST NOT** be placed inside a `panels/` directory (e.g. `panels/console/ConsoleSettings.ts`). * **Reason:** Panel `-meta.ts` files (e.g. `panels/console/console-meta.ts`) need to import the `SettingDescriptor` to call `SettingsUI.SettingUIRegistration.register(descriptor, uiDescriptor)`. `-meta.ts` files are loaded early and **MUST NOT import from `panels/`** (which would break lazy-loading of panel bundles). Since `ui/settings/` is in the `ui/` layer below `panels/`, `-meta.ts` files can safely import from `ui/settings/`. * **CRITICAL RULE — MUST NOT BE IN A `-meta.ts` FILE:** * A `SettingDescriptor` **MUST NOT** be placed in a `-meta.ts` file. * **Reason:** Actual runtime code (e.g. models, panels, SDKs) needs to import the descriptor directly to call `Settings.instance().resolve(descriptor)`. Preload `-meta.ts` files are meant for lazy-loaded extension registrations and must not be imported by runtime code to avoid circular dependencies and module boundary violations. ### 2. `SettingUIDescriptor` Location (Higher-Level `-meta.ts` File) * A `SettingUIDescriptor` defines UI-specific properties (category, title, tags, options, reload requirement, etc.). * **Higher-Level Meta File Rule:** UI registrations **MUST BE PLACED IN A `-meta.ts` FILE ON A HIGHER LEVEL** (e.g., `entrypoints/main/main-meta.ts`, `entrypoints/inspector_main/inspector_main-meta.ts`, `panels/settings/settings-meta.ts`, or the specific panel's `-meta.ts` file). * **CRITICAL RULE — MUST NOT REMAIN IN `core/` OR `models/`:** * UI descriptors **MUST NOT** remain in or be added to `-meta.ts` files under `core/` or `models/` (such as `core/sdk/sdk-meta.ts` or `models/*/*-meta.ts`). * **Goal:** A major goal of this migration is to completely eliminate `-meta.ts` files in `core/` and `models/`. ### 3. Resolving Settings vs `moduleSetting(name)` * Legacy code retrieves settings using string identifiers: `Settings.instance().moduleSetting('setting-name')`. * Refactored code should replace `moduleSetting('setting-name')` with `Settings.instance().resolve(settingDescriptor)`. --- ## Step-by-Step Migration Workflow Given a setting name (e.g., `'preserve-console-log'` or `'network-messages'`): ### Step 1: Locate Existing Registration & Analyze Use-Sites 1. Search for the setting name in `-meta.ts` files to find its `Common.Settings.registerSettingExtension` block. 2. Search for all occurrences of `'setting-name'` or `moduleSetting('setting-name')` across the codebase to identify all use-sites. 3. Determine the **lowest layer** among all use-sites: * If used in `core/sdk` -> Target directory is `core/sdk/`. * If used in `models/` -> Target directory is `models/<module>/`. * If used in a panel (`panels/foo`) or UI -> Target directory is **`ui/settings/`** (file: `ui/settings/FooSettings.ts`). **NEVER place descriptors in `panels/`**. --- ### Step 2: Extract & Define `SettingDescriptor` in the Lowest Layer #### Decision Strategy: Create vs. Update File When placing a `SettingDescriptor` in the target directory, decide whether to create or update a file using these rules: 1. **For Panel / UI Settings (Target is `ui/settings/`)**: * **UPDATE**: Check if `ui/settings/FooSettings.ts` already exists (e.g., `ui/settings/ConsoleSettings.ts`). If so, add and export the `SettingDescriptor` there. * **CREATE**: If `ui/settings/FooSettings.ts` does not exist: * Create `ui/settings/FooSettings.ts`. * Add `FooSettings.ts` to `sources` in `ui/settings/BUILD.gn`. * Export `FooSettings.ts` from `ui/settings/settings.ts`. 2. **For Core / Model Settings (Target is `core/` or `models/`)**: * **UPDATE**: Check if a module-wide settings file exists in that directory (e.g. `core/sdk/SDKSettings.ts`, `models/workspace/WorkspaceSettings.ts`). If so, add and export the `SettingDescriptor` there. * **UPDATE**: If no module settings file exists and the setting is strictly used inside a single file (e.g., `ResourceTreeModel.ts`), update that `.ts` file by exporting the `SettingDescriptor` at the top. * **CREATE**: Otherwise, create `<Module>Settings.ts` (e.g., `core/sdk/SDKSettings.ts`), add it to `sources` in `BUILD.gn`, and export it from the module's entrypoint (`sdk.ts`). #### Code Definition Example: Define and export the `SettingDescriptor` in the target file: ```typescript import type * as Common from '../core/common/common.js'; export const preserveConsoleLogSettingDescriptor: Common.Settings.SettingDescriptor<boolean> = { name: 'preserve-console-log', type: Common.Settings.SettingType.BOOLEAN, defaultValue: false, storageType: Common.Settings.SettingStorageType.SYNCED, }; ``` *Note: For conditional settings (dependent on `hostConfig`), use `Common.Settings.ConditionalSettingDescriptor<ValueT, ReasonT>` with an `isAvailable` function.* --- ### Step 3: Move UI Registration to a Higher-Level `-meta.ts` File If the original registration was in a `core/` or `models/` `-meta.ts` file (e.g., `core/sdk/sdk-meta.ts`), **MOVE** the UI registration to a higher-level `-meta.ts` file (e.g., `entrypoints/main/main-meta.ts` or `panels/console/console-meta.ts`). In the higher-level `-meta.ts` file: 1. Import `SettingsUI` from `ui/settings/settings.js` (e.g., `import * as SettingsUI from '../../ui/settings/settings.js';`). 2. Import the `SettingDescriptor` (from `core/sdk/`, `models/`, or `ui/settings/`). 3. Register using `SettingsUI.SettingUIRegistration.register(...)`: ```typescript import * as SettingsUI from '../../ui/settings/settings.js'; import * as SDK from '../../core/sdk/sdk.js'; SettingsUI.SettingUIRegistration.register(SDK.SDKSettings.preserveConsoleLogSettingDescriptor, { category: Common.Settings.SettingCategory.CONSOLE, title: i18nLazyString(UIStrings.preserveLogUponNavigation), options: [ { value: true, title: i18nLazyString(UIStrings.preserveLogUponNavigation), }, { value: false, title: i18nLazyString(UIStrings.doNotPreserveLogUponNavigation), }, ], }); ``` 4. **Delete** the old `Common.Settings.registerSettingExtension` call from the `core/` or `models/` `-meta.ts` file. (If the `-meta.ts` file becomes empty, delete the file and clean up its build references). --- ### Step 4: Update Call Sites (`moduleSetting` to `resolve`) Find all call sites referencing the setting via `moduleSetting`: ```typescript // BEFORE: const setting = Common.Settings.Settings.instance().moduleSetting('preserve-console-log'); // AFTER: import { preserveConsoleLogSettingDescriptor } from './SDKSettings.js'; ... const setting = Common.Settings.Settings.instance().resolve(preserveConsoleLogSettingDescriptor); ``` *For conditional settings, use `Common.Settings.Settings.instance().maybeResolve(descriptor)` instead of `resolve(descriptor)`.* --- ### Step 5: Update `BUILD.gn` Files and Module Entrypoints 1. If a new `.ts` file was created (e.g., `SDKSettings.ts` or `ui/settings/ConsoleSettings.ts`): * Add the file to `sources` in its module's `BUILD.gn`. * Export the file from the module's entrypoint (`sdk.ts`, `settings.ts`, etc.). 2. If a `core/` or `models/` `-meta.ts` file was deleted, remove it from `BUILD.gn` and `devtools_grd_files.gni`. 3. Verify module imports strictly follow DevTools import rules (refer to `devtools-imports` skill). --- ### Step 6: Verify Changes 1. Run `autoninja -C out/Default` to check GN build. 2. Run `npm run lint` to check style and formatting rules. 3. Run relevant unit tests for the modified module. --- ## Concrete Examples ### Example 1: Core/SDK Setting Migration (`preserve-console-log`) #### Before Migration Setting registered in **`front_end/core/sdk/sdk-meta.ts`** *(Legacy core meta file)*: ```typescript Common.Settings.registerSettingExtension({ category: Common.Settings.SettingCategory.CONSOLE, storageType: Common.Settings.SettingStorageType.SYNCED, title: i18nLazyString(UIStrings.preserveLogUponNavigation), settingName: 'preserve-console-log', settingType: Common.Settings.SettingType.BOOLEAN, defaultValue: false, options: [...], }); ``` Setting used in **`front_end/core/sdk/ResourceTreeModel.ts`**: ```typescript const setting = Common.Settings.Settings.instance().moduleSetting('preserve-console-log'); ``` --- #### After Migration 1. **`front_end/core/sdk/SDKSettings.ts`** *(Lowest Layer in Core — NOT a `-meta.ts` file)*: ```typescript import type * as Common from '../common/common.js'; export const preserveConsoleLogSettingDescriptor: Common.Settings.SettingDescriptor<boolean> = { name: 'preserve-console-log', type: Common.Settings.SettingType.BOOLEAN, defaultValue: false, storageType: Common.Settings.SettingStorageType.SYNCED, }; ``` 2. **`front_end/entrypoints/main/main-meta.ts`** *(Higher-Level Meta File — NOT in `core/` or `models/`)*: ```typescript import * as SDK from '../../core/sdk/sdk.js'; import * as SettingsUI from '../../ui/settings/settings.js'; SettingsUI.SettingUIRegistration.register(SDK.SDKSettings.preserveConsoleLogSettingDescriptor, { category: Common.Settings.SettingCategory.CONSOLE, title: i18nLazyString(UIStrings.preserveLogUponNavigation), options: [...], }); ``` 3. **`front_end/core/sdk/ResourceTreeModel.ts`** *(Call Site in `core/sdk`)*: ```typescript import { preserveConsoleLogSettingDescriptor } from './SDKSettings.js'; const setting = Common.Settings.Settings.instance().resolve(preserveConsoleLogSettingDescriptor); ``` 4. **`front_end/core/sdk/sdk-meta.ts`**: Registration for `'preserve-console-log'` removed. --- ### Example 2: Panel UI Setting Migration (`network-messages`) #### Before Migration Setting registered in **`front_end/panels/console/console-meta.ts`**: ```typescript Common.Settings.registerSettingExtension({ category: Common.Settings.SettingCategory.CONSOLE, storageType: Common.Settings.SettingStorageType.SYNCED, title: i18nLazyString(UIStrings.networkMessages), settingName: 'network-messages', settingType: Common.Settings.SettingType.BOOLEAN, defaultValue: true, options: [...], }); ``` Setting used in **`front_end/panels/console/ConsoleView.ts`**: ```typescript const setting = Common.Settings.Settings.instance().moduleSetting('network-messages'); ``` --- #### After Migration 1. **`front_end/ui/settings/ConsoleSettings.ts`** *(Lowest Layer for UI/Panel Descriptor — NOT in `panels/console/`!)*: ```typescript import type * as Common from '../../core/common/common.js'; export const networkMessagesSettingDescriptor: Common.Settings.SettingDescriptor<boolean> = { name: 'network-messages', type: Common.Settings.SettingType.BOOLEAN, defaultValue: true, storageType: Common.Settings.SettingStorageType.SYNCED, }; ``` 2. **`front_end/panels/console/console-meta.ts`** *(Panel Meta File — Imports Descriptor from `ui/settings/`)*: ```typescript import * as SettingsUI from '../../ui/settings/settings.js'; SettingsUI.SettingUIRegistration.register(SettingsUI.ConsoleSettings.networkMessagesSettingDescriptor, { category: Common.Settings.SettingCategory.CONSOLE, title: i18nLazyString(UIStrings.networkMessages), options: [...], }); ``` 3. **`front_end/panels/console/ConsoleView.ts`** *(Call Site in `panels/console`)*: ```typescript import * as SettingsUI from '../../ui/settings/settings.js'; const setting = Common.Settings.Settings.instance().resolve(SettingsUI.ConsoleSettings.networkMessagesSettingDescriptor); ``` --- ## Interface Reference Summary ### `SettingDescriptor<T>` Defined in `core/common/Settings.ts`: * `name: string`: Unique setting name (kebab-case). * `type: SettingType`: `BOOLEAN`, `ENUM`, `ARRAY`, or `REGEX`. * `defaultValue: ValueT | ((hostConfig: HostConfig) => ValueT)`: Default setting value. * `storageType?: SettingStorageType`: `SYNCED`, `LOCAL`, `GLOBAL`, or `SESSION`. ### `SettingUIDescriptor` Defined in `ui/settings/SettingUIRegistration.ts`: * `category?: SettingCategory`: Category under which setting is listed in Settings UI. * `order?: number`: Sorting order. * `title?: () => LocalizedString`: Title string displayed in Settings UI. * `tags?: Array<() => LocalizedString>`: Search tags for Command Menu. * `options?: SettingExtensionOption[]`: Enum / boolean option descriptions. * `reloadRequired?: boolean`: Whether setting change requires DevTools reload. * `deprecationNotice?: { disabled: boolean, warning: () => LocalizedString, experiment?: string }`: Deprecation notice. * `learnMore?: LearnMore`: Help link or tooltip info.