From d8c84650ba975df38d9f6eb44769ab4f14e33344 Mon Sep 17 00:00:00 2001 From: Alex Lavrov <36633600+alexslavr@users.noreply.github.com> Date: Thu, 17 Sep 2026 16:38:01 +0400 Subject: [PATCH 1/4] chore: update wrapper regeneration instructions and clarify public API change process --- .github/copilot-instructions.md | 10 +-- .../public-api-wrappers.instructions.md | 64 +++++++++++++++++ .../wrapper-angular.instructions.md | 72 +++++++++++++++++++ .../wrapper-react.instructions.md | 70 ++++++++++++++++++ .../instructions/wrapper-vue.instructions.md | 62 ++++++++++++++++ .github/prompts/update-wrappers.prompt.md | 41 +++++++++++ CLAUDE.md | 21 +++--- 7 files changed, 324 insertions(+), 16 deletions(-) create mode 100644 .github/instructions/public-api-wrappers.instructions.md create mode 100644 .github/instructions/wrapper-angular.instructions.md create mode 100644 .github/instructions/wrapper-react.instructions.md create mode 100644 .github/instructions/wrapper-vue.instructions.md create mode 100644 .github/prompts/update-wrappers.prompt.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index abfb6134970c..d78250b6f372 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -183,7 +183,7 @@ pnpm nx clean:artifacts devextreme # build artifacts only ``` packages/ devextreme/ # core library: ui/, viz/, core/, data/, __internal/ - devextreme-{angular,react,vue}/ # framework wrappers (generated) + devextreme-{angular,react,vue}/ # framework wrappers (hand/AI-maintained) devextreme-scss/ # SCSS themes devextreme-themebuilder/ # theme builder devextreme-metadata/ # metadata for wrapper generation @@ -209,14 +209,14 @@ clean (`devextreme-nx-infra-plugin:clean` preserving CSS and npm metadata) → l - Use `pnpm nx ` rather than raw npm scripts so Nx caching and the dependency graph stay correct. - Build before testing: `pnpm nx build:dev devextreme`; QUnit and TestCafe both require an up-to-date build. -- Run `pnpm run regenerate-all` after editing wrapper generators, TypeScript declarations, or `devextreme-internal-tools`. -- Edit source files only under `packages/devextreme/js/**`, `packages/devextreme-scss/scss/**`, and `packages/devextreme-metadata/**`. +- The Angular/React/Vue wrappers are maintained by hand (with AI agents), not regenerated from metadata. When public API in `packages/devextreme/js/{ui,viz}/**/*.d.ts` changes, update all three wrappers in the same PR following `.github/instructions/wrapper-{angular,react,vue}.instructions.md` and `.github/instructions/public-api-wrappers.instructions.md`. +- Edit source files under `packages/devextreme/js/**`, `packages/devextreme-scss/scss/**`, `packages/devextreme-metadata/**`, and the wrapper `packages/devextreme-{angular,react,vue}/src/**` (except each package's `src/core/**`). - Match the Node and pnpm versions declared in `package.json` (`engines`, `packageManager`); mismatched versions cause CI failure. - Set `DEVEXTREME_TEST_CI=true` for test-mode builds and `BUILD_TEST_INTERNAL_PACKAGE=true` for wrapper test prep. ## Constraints -- NEVER edit generated wrappers under `packages/devextreme-{angular,react,vue}/src/` (templates excepted); update the generators and run `pnpm run regenerate-all` instead. +- The wrappers under `packages/devextreme-{angular,react,vue}/src/` are maintained by hand — keep them in sync with the public API per the wrapper instructions; NEVER edit each package's `src/core/**` (hand-written base classes) to accommodate a single component. - NEVER hand-edit `packages/devextreme/js/__internal/core/localization/{default_messages.ts,cldr-data/**}`; regenerate via the localization executor. - NEVER run `pnpm install` without `--frozen-lockfile`; use `pnpm install --frozen-lockfile` to match CI. @@ -224,7 +224,7 @@ clean (`devextreme-nx-infra-plugin:clean` preserving CSS and npm metadata) → l - Always install with `pnpm install --frozen-lockfile`; never plain `pnpm install`. - Build before test: `pnpm nx build:dev devextreme`. -- Generated wrappers under `packages/devextreme-{angular,react,vue}/src/` are read-only — modify generators and run `pnpm run regenerate-all`. +- Wrappers under `packages/devextreme-{angular,react,vue}/src/` are hand/AI-maintained — update them per the wrapper instructions when public API changes; leave each `src/core/**` alone. - Prefer `pnpm nx ` over direct npm scripts for caching. - Consult @.github/instructions/ for file-specific coding rules before editing. diff --git a/.github/instructions/public-api-wrappers.instructions.md b/.github/instructions/public-api-wrappers.instructions.md new file mode 100644 index 000000000000..c56fa466e999 --- /dev/null +++ b/.github/instructions/public-api-wrappers.instructions.md @@ -0,0 +1,64 @@ +--- +applyTo: "**/packages/devextreme/js/{ui,viz}/**/*.d.ts" +--- + +# Public API changes must be propagated to the framework wrappers + +The Angular, React, and Vue wrappers are **no longer regenerated** from +`devextreme-metadata` / `devextreme-internal-tools`. They are maintained by hand (with the +help of local AI agents). This means **every public API change in a wrapped component's +`.d.ts` must be applied, in the same pull request, to all three wrappers**. + +This file applies to the public API surface (`js/ui/**/*.d.ts`, `js/viz/**/*.d.ts`). Use it +both when editing the API and when reviewing an API change. + +## What counts as a wrapper-affecting change + +A change to any of the following in a component's options interface (e.g. `dxButtonOptions`, +its `Properties` alias, or a nested option type such as `dxDataGridColumn`) affects the +wrappers: + +- **Options** — a property added, removed, renamed, or retyped on the options interface. +- **Events** — an `onX` callback added, removed, renamed, or whose event type changed. +- **Event types** — a `*Event` type (e.g. `ClickEvent`) whose shape changed. +- **Nested option types** — a collection/object option type (e.g. `columns`, `items`, + `toolbar`) added, removed, renamed, or restructured. +- **Template options** — a `template` option added or removed. + +Purely internal changes (implementation in `js/__internal/**`, JSDoc-only edits that do not +change types or names) do **not** require a wrapper change. + +## Where each change lands in the wrappers + +| Public API element | Angular | React | Vue | +|---|---|---|---| +| Option `foo?: T` | `@Input() get foo()/set foo()` in `ui//index.ts` | field in the component's options type (`IXOptions`) | entry in `props` (correct runtime type + `PropType`) and `emits` `update:foo` | +| Event `onX?: (e: XEvent) => void` | `@Output() onX: EventEmitter` + `{ subscribe, emit }` entry | narrowed field in `IXOptionsNarrowedEvents` + entry in `independentEvents` | `onX` prop typed `Function as PropType<(e: XEvent) => void>` | +| Nested option type | do **not** edit `ui/nested/**` (deprecated, removed in a parallel PR) | `NestedOption` config component | configuration component via `prepareConfigurationComponentConfig` | +| Template option | template input | entry in `templateProps` | `template` prop | + +The per-framework anatomy and rules live in +[wrapper-angular.instructions.md](./wrapper-angular.instructions.md), +[wrapper-react.instructions.md](./wrapper-react.instructions.md), and +[wrapper-vue.instructions.md](./wrapper-vue.instructions.md). + +## Reviewer checklist + +When a PR changes a wrapped component's public `.d.ts`, verify and comment inline if any is +missing: + +1. **Completeness** — every added, removed, or renamed option/event is reflected in + **all three** wrappers (`devextreme-angular/src`, `devextreme-react/src`, + `devextreme-vue/src`). A change present in one or two wrappers but not the third is a + defect — flag the missing wrapper(s) by name. +2. **Type fidelity** — the wrapper carries the same type as the API (including union + members, generics, and the correct Vue runtime type + `PropType`). +3. **Breaking changes** — explicitly label these as **breaking** in a review comment: + - an option or event **removed** or **renamed**; + - an option/event **type narrowed** or otherwise changed incompatibly; + - a nested option type removed or restructured. + Confirm the wrappers reflect the same removal/rename so they stay in sync; do not let a + breaking API change land with stale wrapper surface. + +Only comment on the wrappers and the API contract. Do not propose regenerating the wrappers +or restoring the metadata pipeline — that workflow is retired. diff --git a/.github/instructions/wrapper-angular.instructions.md b/.github/instructions/wrapper-angular.instructions.md new file mode 100644 index 000000000000..1967d75ff38e --- /dev/null +++ b/.github/instructions/wrapper-angular.instructions.md @@ -0,0 +1,72 @@ +--- +applyTo: "**/packages/devextreme-angular/src/**" +--- + +# Maintaining the DevExtreme Angular wrapper by hand + +The files under `packages/devextreme-angular/src/` are **no longer generated**. Edit them +directly to keep them in sync with the public API in +`packages/devextreme/js/{ui,viz}/**/*.d.ts`. See +[public-api-wrappers.instructions.md](./public-api-wrappers.instructions.md) for when a +wrapper change is required and what the reviewer checks. + +## Do not edit + +- `packages/devextreme-angular/src/core/**` — hand-written base classes + (`DxComponent`, `NestedOptionHost`, template/integration modules). Never change these to + accommodate a single component. +- `packages/devextreme-angular/src/ui/nested/**` — the legacy nested option components + ("old nesteds"). This folder is **deprecated and being removed in a parallel PR**. Do not + add, edit, or reference files here, even when a nested/collection option changes. + +## Component file anatomy (`ui//index.ts`) + +Use the widget's options interface (e.g. `dxButtonOptions` and its `WidgetOptions` base) as +the source of truth. A component file contains, in order: + +1. **Imports** — Angular symbols (`Component`, `Input`, `Output`, `EventEmitter`, + `NgModule`, `OnDestroy`, …), `type` imports of the event types and enum/union types from + `devextreme/ui/` and `devextreme/common`, the default widget import + (`import DxButton from 'devextreme/ui/button'`), and base classes from + `devextreme-angular/core`. +2. **`@Component` decorator** — `selector: 'dx-'`, `template` (`` for + container widgets, empty otherwise), `imports: [ DxIntegrationModule ]`, and the standard + `providers` (`DxTemplateHost`, `WatcherHelper`, `NestedOptionHost`). +3. **Class** `DxComponent extends DxComponent implements OnDestroy` with + `instance: Dx = null;`. +4. **One `@Input()` getter/setter pair per option**, in the same order as the options + interface (including inherited `WidgetOptions`/`DOMComponentOptions`): + ```ts + @Input() + get text(): string { return this._getOption('text'); } + set text(value: string) { this._setOption('text', value); } + ``` + The getter/setter type must match the option's type exactly (keep `| undefined` and + union members). +5. **Events** — one `@Output()` per `onX` callback, named exactly as the option and typed + with the event type: + ```ts + @Output() onClick: EventEmitter; + ``` +6. **Two-way binding outputs** — one `@Output()