Skip to content

Scheduler — Add ESLint naming-convention rule to forbid underscore-prefixed members#33126

Open
aleksei-semikozov wants to merge 18 commits intoDevExpress:26_1from
aleksei-semikozov:eslint-naming-convention
Open

Scheduler — Add ESLint naming-convention rule to forbid underscore-prefixed members#33126
aleksei-semikozov wants to merge 18 commits intoDevExpress:26_1from
aleksei-semikozov:eslint-naming-convention

Conversation

@aleksei-semikozov
Copy link
Copy Markdown
Contributor

No description provided.

@aleksei-semikozov aleksei-semikozov force-pushed the eslint-naming-convention branch from 2b90428 to d3a8e3c Compare April 2, 2026 03:13
@aleksei-semikozov aleksei-semikozov marked this pull request as ready for review April 2, 2026 09:56
Copilot AI review requested due to automatic review settings April 2, 2026 09:57
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR tightens linting for the internal Scheduler TypeScript code by forbidding underscore-prefixed member names, while allowing a curated set of legacy/override members to keep existing code compliant.

Changes:

  • Add a Scheduler-specific allowlist and regex to exempt known legacy/override underscore members from the naming-convention rule.
  • Change @typescript-eslint/naming-convention for Scheduler to forbid underscore-prefixed memberLike identifiers by default.
  • Add a temporary override to still allow underscore-prefixed members under js/__internal/scheduler/appointments/** pending refactoring.

Comment on lines +185 to +186
const schedulerMemberAllowlistRegex =
`^(_|__esModule|${schedulerMemberAllowlist.map(s => s.replace(/\$/g, '\\$')).join('|')})$`;
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

schedulerMemberAllowlistRegex only escapes $ in allowlisted member names. That makes the regex fragile: if any current/future allowlist entry contains other regex metacharacters (e.g. ., +, ?, [), the filter will misbehave and may accidentally allow/forbid the wrong identifiers. Consider using a small helper to escape all regex metacharacters for each allowlisted string before joining them into the alternation.

Suggested change
const schedulerMemberAllowlistRegex =
`^(_|__esModule|${schedulerMemberAllowlist.map(s => s.replace(/\$/g, '\\$')).join('|')})$`;
function escapeRegex(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
const schedulerMemberAllowlistRegex =
`^(_|__esModule|${schedulerMemberAllowlist.map(escapeRegex).join('|')})$`;

Copilot uses AI. Check for mistakes.
Comment on lines +741 to +757
'@typescript-eslint/naming-convention': [
'error',
{
selector: ['variable', 'function', 'parameter'],
format: null,
leadingUnderscore: 'forbid',
filter: {
regex: '^_$',
match: false,
},
},
{
selector: 'memberLike',
format: null,
leadingUnderscore: 'allow',
},
],
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The @typescript-eslint/naming-convention configuration for scheduler is duplicated here just to change memberLike.leadingUnderscore behavior. To avoid the two blocks drifting over time, consider extracting the shared naming-convention options (e.g., the variable/function/parameter selector config) into a reusable constant and reusing it in both places.

Copilot uses AI. Check for mistakes.
@aleksei-semikozov aleksei-semikozov requested a review from a team as a code owner April 2, 2026 10:58
Copilot AI review requested due to automatic review settings April 2, 2026 11:28
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 7 comments.


export class ViewDataGeneratorDay extends ViewDataGenerator {
_calculateStartViewDate(options) {
calculateStartViewDate(options) {
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

calculateStartViewDate overrides a protected method on ViewDataGenerator, but without an explicit modifier it becomes public here. Mark it as protected to avoid accidentally expanding the public API surface and to keep access levels consistent across the hierarchy.

Suggested change
calculateStartViewDate(options) {
protected calculateStartViewDate(options) {

Copilot uses AI. Check for mistakes.
}

_calculateStartViewDate(options) {
calculateStartViewDate(options) {
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

calculateStartViewDate is protected in the base ViewDataGenerator, but this override is implicitly public. Please add the protected modifier to keep the same accessibility as the base method.

Suggested change
calculateStartViewDate(options) {
protected calculateStartViewDate(options) {

Copilot uses AI. Check for mistakes.
}

_calculateStartViewDate(options) {
calculateStartViewDate(options) {
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

calculateStartViewDate is protected in the base class, but this override is implicitly public. Add protected to keep the intended visibility and avoid widening the API surface.

Suggested change
calculateStartViewDate(options) {
protected calculateStartViewDate(options) {

Copilot uses AI. Check for mistakes.
}

_calculateStartViewDate(options: any) {
calculateStartViewDate(options: any) {
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

calculateStartViewDate overrides a protected base method, but is implicitly public here. Consider adding the protected modifier to keep access levels consistent.

Suggested change
calculateStartViewDate(options: any) {
protected calculateStartViewDate(options: any) {

Copilot uses AI. Check for mistakes.
}

_calculateStartViewDate(options) {
calculateStartViewDate(options) {
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

calculateStartViewDate overrides a protected method from ViewDataGenerator, but is implicitly public here. Add protected to avoid exposing it as a public method.

Suggested change
calculateStartViewDate(options) {
protected calculateStartViewDate(options) {

Copilot uses AI. Check for mistakes.
}

_setVisibilityDates(options) {
setVisibilityDates(options) {
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setVisibilityDates is protected in ViewDataGenerator, but this override is implicitly public. Mark it as protected to keep the same accessibility as the base method.

Suggested change
setVisibilityDates(options) {
protected setVisibilityDates(options) {

Copilot uses AI. Check for mistakes.
Comment on lines +234 to 236
protected getAllDayHeight(showAllDayPanel) {
return getAllDayHeight(showAllDayPanel, true, this.DOMMetaData);
}
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new getAllDayHeight method has the same name as the module-level getAllDayHeight helper, which makes the implementation read like a recursive call. Consider aliasing the helper (e.g., getAllDayHeight as calculateAllDayHeight) or renaming the method to avoid the name collision.

Copilot uses AI. Check for mistakes.
Copilot AI review requested due to automatic review settings April 2, 2026 12:54
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comment on lines +146 to +147
export const schedulerMemberAllowlistRegex =
`^(_|__esModule|${schedulerMemberAllowlist.map(s => s.replace(/\$/g, '\\$')).join('|')})$`;
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The $ escaping in schedulerMemberAllowlistRegex looks incorrect: s.replace(/\$/g, '\\$') produces \\$ in the final regex source, which will be parsed by RegExp as a literal backslash followed by an end-of-string anchor (so allowlisted members like _$dateTable won’t actually match the filter). This will cause @typescript-eslint/naming-convention to report underscore-prefixed DOM members that contain $.

Use a single regex-escape for $ in the generated pattern (i.e., ensure the final pattern contains \$, not \\$), or switch to a general regex-escape helper for all special characters.

Copilot uses AI. Check for mistakes.
Copilot AI review requested due to automatic review settings April 2, 2026 13:11
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

packages/devextreme/js/__internal/scheduler/workspaces/helpers/m_position_helper.ts:243

  • getMaxAllowedVerticalPosition appears to be an internal helper (previously underscore-prefixed) and is only used inside these strategy classes. Consider making it protected (or private) to preserve encapsulation and prevent other code from depending on it as a public method.
  getMaxAllowedVerticalPosition({
    groupIndex,
    showAllDayPanel,
    isGroupedAllDayPanel,
  }) {
    const { rowIndex } = this.viewDataProvider.getLastGroupCellPosition(groupIndex);

}

_calculateCellIndex(rowIndex, columnIndex, rowCount, columnCount) {
calculateCellIndex(rowIndex, columnIndex, rowCount, columnCount) {
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

calculateCellIndex overrides a protected method in ViewDataGenerator, but is currently public (no modifier). Mark it protected to match the base class visibility and avoid widening the API surface.

Suggested change
calculateCellIndex(rowIndex, columnIndex, rowCount, columnCount) {
protected calculateCellIndex(rowIndex, columnIndex, rowCount, columnCount) {

Copilot uses AI. Check for mistakes.
Comment on lines +142 to +143
export const schedulerMemberAllowlistRegex =
`^(_|__esModule|${schedulerMemberAllowlist.map(s => s.replace(/\$/g, '\\$')).join('|')})$`;
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The allowlist regex escapes only $. If any future allowlisted member name includes other regex meta-characters (e.g. ., ?, +, |, (), this will break the regex or change its meaning. Consider escaping all regex-special characters when building the pattern (or using a dedicated escape helper) to make this list safer to maintain.

Suggested change
export const schedulerMemberAllowlistRegex =
`^(_|__esModule|${schedulerMemberAllowlist.map(s => s.replace(/\$/g, '\\$')).join('|')})$`;
const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
export const schedulerMemberAllowlistRegex =
`^(_|__esModule|${schedulerMemberAllowlist.map(s => escapeRegex(s)).join('|')})$`;

Copilot uses AI. Check for mistakes.
Copilot AI review requested due to automatic review settings April 2, 2026 20:16
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 23 out of 23 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (3)

packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_generator_timeline_month.ts:25

  • calculateStartViewDate is a protected hook in ViewDataGenerator, but this override is currently public (no access modifier). After dropping the underscore prefix, please consider marking it protected to keep the method as an internal extension point rather than a public API.
  calculateStartViewDate(options: any) {
    return timelineMonthUtils.calculateStartViewDate(
      options.currentDate,
      options.startDayHour,
      options.startDate,
      options.intervalCount,
    );
  }

packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_generator_month.ts:70

  • calculateStartViewDate overrides a protected hook from ViewDataGenerator, but it’s declared without an access modifier here (so it becomes public). After dropping the underscore prefix, consider marking this override as protected to keep it as an internal extension point.
  calculateStartViewDate(options) {
    return monthUtils.calculateStartViewDate(
      options.currentDate,
      options.startDayHour,
      options.startDate,

packages/devextreme/js/__internal/scheduler/workspaces/view_model/m_view_data_generator_month.ts:80

  • setVisibilityDates overrides a protected hook from ViewDataGenerator, but it’s declared without an access modifier here (so it becomes public). Consider making this method protected to avoid expanding the class’s public API now that the underscore prefix is removed.
  setVisibilityDates(options) {
    const {
      intervalCount,
      startDate,
      currentDate,

Comment on lines +11 to 18
calculateStartViewDate(options) {
return weekUtils.calculateStartViewDate(
options.currentDate,
options.startDayHour,
options.startDate,
this._getIntervalDuration(options.intervalCount),
this.getIntervalDuration(options.intervalCount),
this.getFirstDayOfWeek(options.firstDayOfWeek),
);
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

calculateStartViewDate overrides a protected hook from ViewDataGenerator, but here it is declared without an access modifier (making it public). Since the underscore prefix was removed, this unintentionally widens the apparent public API of the generator; consider marking this override as protected (matching the base class visibility) to keep it as an internal customization point.

Copilot uses AI. Check for mistakes.
Comment on lines +5 to 12
calculateStartViewDate(options) {
return dayUtils.calculateStartViewDate(
options.currentDate,
options.startDayHour,
options.startDate,
this._getIntervalDuration(options.intervalCount),
this.getIntervalDuration(options.intervalCount),
);
}
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

calculateStartViewDate overrides a protected hook from ViewDataGenerator, but it is currently declared without an access modifier (so it becomes public). With the underscore removed, it’s better to keep this as protected to avoid exposing it as part of the public surface of the class.

Copilot uses AI. Check for mistakes.
Comment on lines +11 to 18
calculateStartViewDate(options) {
return workWeekUtils.calculateStartViewDate(
options.currentDate,
options.startDayHour,
options.startDate,
this._getIntervalDuration(options.intervalCount),
this.getIntervalDuration(options.intervalCount),
this.getFirstDayOfWeek(options.firstDayOfWeek),
);
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

calculateStartViewDate overrides a protected hook from the base generator, but it is declared as public here (no access modifier). To avoid widening the class API after removing the underscore prefix, consider marking it protected to match ViewDataGenerator.

Copilot uses AI. Check for mistakes.
Copilot AI review requested due to automatic review settings April 2, 2026 20:52
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 26 out of 26 changed files in this pull request and generated 3 comments.

appointmentDataSource!: AppointmentDataSource;

_dataSource: any;
dataSource: any;
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scheduler includes DataHelperMixin, which reads/writes the data source via this._dataSource. Renaming the instance field to dataSource means the mixin will still populate this._dataSource, while the scheduler code will read this.dataSource (undefined), breaking loading, filtering, and reload flows. Consider keeping the _dataSource field (or adding a dataSource getter/setter that proxies to _dataSource) and updating the ESLint allowlist instead of renaming the storage field.

Suggested change
dataSource: any;
_dataSource: any;

Copilot uses AI. Check for mistakes.
@@ -741,7 +744,7 @@ class Scheduler extends SchedulerOptionsBaseWidget {
visible && this._dimensionChanged(null, true);
}

Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DataHelperMixin expects override hooks named _dataSourceOptions and _dataSourceChangedHandler (see js/__internal/data/m_data_helper.ts). Renaming these to dataSourceOptions / dataSourceChangedHandler means the mixin will no longer call them, so data source options won’t be applied and change notifications won’t refresh appointments. Recommendation: keep the underscore-prefixed hook names (and allowlist them for the scheduler naming-convention rule) or provide underscore-named wrappers that delegate to the new methods.

Suggested change
_dataSourceOptions() {
return this.dataSourceOptions();
}

Copilot uses AI. Check for mistakes.
Comment on lines +84 to +91
// m_scheduler.ts
'_appointments',
'_dataAccessors',
'_getDragBehavior',
'_isAppointmentBeingUpdated',
'_options',
'_workSpace',

Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The scheduler naming-convention allowlist is missing DataHelperMixin hook/member names (_dataSource, _dataSourceOptions, _dataSourceChangedHandler). Because scheduler widgets include DataHelperMixin, these underscore members need to remain available to preserve behavior; otherwise you end up renaming required mixin hooks in m_scheduler.ts. Suggest adding these names to schedulerLegacyMembers (or a dedicated list) so the lint rule can forbid other underscore members without breaking DataHelperMixin integration.

Copilot uses AI. Check for mistakes.
Copilot AI review requested due to automatic review settings April 2, 2026 22:47
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 26 out of 26 changed files in this pull request and generated 2 comments.

Comment on lines 744 to 750
visible && this._dimensionChanged(null, true);
}

_dataSourceOptions() {
dataSourceOptions() {
return { paginate: false };
}

Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scheduler includes DataHelperMixin, which looks specifically for _dataSourceOptions and _dataSourceChangedHandler methods (see js/__internal/data/m_data_helper.ts). Renaming them to dataSourceOptions / dataSourceChangedHandler means the mixin will no longer call these hooks, and there is still an internal call to this._dataSourceChangedHandler(...) in _initMarkup (around line 1090), which will now be undefined. Please restore the underscore method names (and, if needed for the new lint rule, add them to the scheduler allowlist) or add underscore-named wrapper methods delegating to the new names, and make sure all call sites use the same method names.

Copilot uses AI. Check for mistakes.
Comment on lines 884 to 892
this._renderContentImpl();
}

_dataSourceChangedHandler(result?: Appointment[]) {
dataSourceChangedHandler(result?: Appointment[]) {
if (this.readyToRenderAppointments) {
this.workSpaceRecalculation.done(() => {
this._layoutManager.prepareAppointments(result);
this.layoutManager.prepareAppointments(result);
this.renderAppointments();
this.updateA11yStatus();
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dataSourceChangedHandler was renamed, but _initMarkup still calls this._dataSourceChangedHandler(...) (around line 1090). Unless the underscore-named method remains (as required by DataHelperMixin), this will throw at runtime. Please align the method name and the call sites; if you keep the underscore method for framework compatibility, consider having dataSourceChangedHandler delegate to _dataSourceChangedHandler (or vice versa) to avoid future drift.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants