or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

component-rules.mdcomputed-property-rules.mdember-utils.mdindex.mdlegacy-configuration.mdmigration-rules.mdmodern-configuration.mdplugin-configuration.mdroute-rules.mdservice-rules.mdtest-rules.md
tile.json

migration-rules.mddocs/

Migration Rules

Rules helping with Ember version migrations and deprecation warnings. These rules assist in upgrading Ember applications by identifying deprecated patterns and encouraging modern alternatives.

Capabilities

Module Import Migration

Rules for migrating to modern ES6 module imports and new Ember module structure.

/**
 * Require new module imports
 * Enforces modern import syntax over global Ember namespace
 */
'ember/new-module-imports': ESLintRule;

/**
 * Disallow old shims
 * Prevents usage of deprecated shim modules
 */
'ember/no-old-shims': ESLintRule;

/**
 * Use Ember Data RFC 395 imports
 * Enforces new Ember Data import paths
 */
'ember/use-ember-data-rfc-395-imports': ESLintRule;

Usage Examples:

// ❌ Bad - old global namespace (triggers new-module-imports)
import Ember from 'ember';

const { Component, computed, inject } = Ember;

export default Component.extend({
  store: inject.service(),
  // ...
});

// ✅ Good - new module imports
import Component from '@ember/component';
import { computed } from '@ember/object';
import { inject as service } from '@ember/service';

export default Component.extend({
  store: service(),
  // ...
});

// ❌ Bad - old shim imports (triggers no-old-shims)
import Ember from 'ember';
import DS from 'ember-data';

// ✅ Good - direct imports
import { computed } from '@ember/object';
import Model, { attr, belongsTo } from '@ember-data/model';

// ❌ Bad - old Ember Data imports (triggers use-ember-data-rfc-395-imports)
import DS from 'ember-data';

export default DS.Model.extend({
  name: DS.attr('string'),
  author: DS.belongsTo('user')
});

// ✅ Good - RFC 395 imports
import Model, { attr, belongsTo } from '@ember-data/model';

export default class Book extends Model {
  @attr('string') name;
  @belongsTo('user') author;
}

Class Migration Rules

Rules for migrating from classic Ember classes to native ES classes.

/**
 * Disallow classic classes
 * Encourages native ES classes over Ember.Object.extend()
 */
'ember/no-classic-classes': ESLintRule;

/**
 * Disallow classic components
 * Promotes Glimmer components over classic components
 */
'ember/no-classic-components': ESLintRule;

/**
 * Disallow Ember super in ES classes
 * Enforces native super() usage in ES classes
 */
'ember/no-ember-super-in-es-classes': ESLintRule;

Usage Examples:

// ❌ Bad - classic class (triggers no-classic-classes)
export default EmberObject.extend({
  init() {
    this._super(...arguments);
    this.setup();
  }
});

// ✅ Good - native ES class
export default class MyClass extends EmberObject {
  constructor() {
    super(...arguments);
    this.setup();
  }
}

// ❌ Bad - classic component (triggers no-classic-components)
export default Component.extend({
  tagName: 'div',
  classNames: ['my-component']
});

// ✅ Good - Glimmer component
export default class MyComponent extends Component {
  // Modern component patterns
}

// ❌ Bad - this._super in ES class (triggers no-ember-super-in-es-classes)
export default class MyRoute extends Route {
  beforeModel() {
    this._super(...arguments);
    // modern logic
  }
}

// ✅ Good - native super in ES class
export default class MyRoute extends Route {
  beforeModel() {
    super.beforeModel(...arguments);
    // modern logic
  }
}

Property and Method Migration

Rules for migrating deprecated property and method patterns.

/**
 * Disallow get/set usage
 * Encourages native property access
 */
'ember/no-get': ESLintRule;

/**
 * Disallow getWithDefault
 * Promotes nullish coalescing operator
 */
'ember/no-get-with-default': ESLintRule;

/**
 * Use get and set appropriately
 * Guidelines for when get/set are still needed
 */
'ember/use-ember-get-and-set': ESLintRule;

Usage Examples:

// ❌ Bad - using get() unnecessarily (triggers no-get)
const userName = this.get('user.name');
const userAge = this.get('user.age');

// ✅ Good - native property access
const userName = this.user.name;
const userAge = this.user.age;

// ❌ Bad - using getWithDefault (triggers no-get-with-default)
const theme = this.getWithDefault('user.preferences.theme', 'light');

// ✅ Good - nullish coalescing
const theme = this.user.preferences?.theme ?? 'light';

// When get/set are still needed (Ember proxy objects, dynamic keys)
// ✅ Good - appropriate use of get/set
const dynamicValue = this.get(dynamicKey);
this.set(dynamicKey, newValue);

Decorator Migration Rules

Rules for migrating to decorator-based patterns.

/**
 * Classic decorator hooks
 * Manages transition from classic to decorator patterns
 */
'ember/classic-decorator-hooks': ESLintRule;

/**
 * Classic decorator no classic methods
 * Prevents mixing decorators with classic method patterns
 */
'ember/classic-decorator-no-classic-methods': ESLintRule;

Usage Examples:

// ❌ Bad - mixing classic and decorator patterns (triggers classic-decorator-no-classic-methods)
export default class MyComponent extends Component {
  @service store;
  
  // Classic method pattern mixed with decorators
  someProperty: computed('data', function() {
    return this.data.length;
  })
}

// ✅ Good - consistent decorator usage
export default class MyComponent extends Component {
  @service store;
  
  get someProperty() {
    return this.data.length;
  }
}

Testing Migration Rules

Rules for migrating to modern testing patterns.

/**
 * Disallow legacy test waiters
 * Promotes modern test waiter patterns
 */
'ember/no-legacy-test-waiters': ESLintRule;

/**
 * Disallow moduleFor test patterns
 * Encourages modern setupTest patterns
 */
'ember/no-test-module-for': ESLintRule;

/**
 * Prefer Ember test helpers
 * Promotes @ember/test-helpers over legacy helpers
 */
'ember/prefer-ember-test-helpers': ESLintRule;

Usage Examples:

// ❌ Bad - legacy test waiter (triggers no-legacy-test-waiters)
import { registerWaiter } from '@ember/test';

registerWaiter(() => {
  return !App.hasPendingRequests();
});

// ✅ Good - modern test waiter
import { registerWaiter } from '@ember/test-waiters';

const waiter = registerWaiter('api-requests');

// ❌ Bad - moduleFor pattern (triggers no-test-module-for)
import { moduleForComponent, test } from 'ember-qunit';

moduleForComponent('my-component', 'Integration | Component | my component', {
  integration: true
});

// ✅ Good - modern setupTest
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';

module('Integration | Component | my-component', function(hooks) {
  setupRenderingTest(hooks);
});

Deprecation and API Migration

Rules for handling deprecated APIs and migrating to modern alternatives.

/**
 * Disallow deprecated router transition methods
 * Enforces modern routing transition methods
 */
'ember/no-deprecated-router-transition-methods': ESLintRule;

/**
 * Disallow current route name
 * Promotes router service usage
 */
'ember/no-current-route-name': ESLintRule;

/**
 * Avoid needs in controllers
 * Discourages deprecated controller dependency injection
 */
'ember/avoid-using-needs-in-controllers': ESLintRule;

Usage Examples:

// ❌ Bad - deprecated transition method (triggers no-deprecated-router-transition-methods)
this.transitionToRoute('user.profile', user.id);

// ✅ Good - modern transition method
this.transitionTo('user.profile', user.id);

// ❌ Bad - currentRouteName usage (triggers no-current-route-name)
if (this.currentRouteName === 'user.profile') {
  // logic
}

// ✅ Good - router service
@service router;

if (this.router.currentRouteName === 'user.profile') {
  // logic
}

// ❌ Bad - controller needs (triggers avoid-using-needs-in-controllers)
export default Controller.extend({
  needs: ['application'],
  
  someAction() {
    this.controllers.application.send('globalAction');
  }
});

// ✅ Good - service injection
export default Controller.extend({
  @service globalActions;
  
  someAction() {
    this.globalActions.performGlobalAction();
  }
});

Library Migration Rules

Rules for migrating from deprecated libraries and patterns.

/**
 * Disallow jQuery usage
 * Promotes native DOM APIs or modifiers
 */
'ember/no-jquery': ESLintRule;

/**
 * Disallow global jQuery
 * Prevents global jQuery usage
 */
'ember/no-global-jquery': ESLintRule;

/**
 * jQuery Ember run integration
 * Ensures jQuery operations are wrapped in Ember.run
 */
'ember/jquery-ember-run': ESLintRule;

Usage Examples:

// ❌ Bad - jQuery usage (triggers no-jquery)
this.$('.my-element').addClass('active');

// ✅ Good - native DOM API or modifiers
this.element.querySelector('.my-element').classList.add('active');

// Or use modifiers in templates
<div class={{if this.isActive "active"}} {{on "click" this.handleClick}}></div>

// ❌ Bad - global jQuery (triggers no-global-jquery)
$('#my-element').fadeIn();

// ✅ Good - avoid jQuery entirely or import explicitly
import $ from 'jquery';
$('#my-element').fadeIn(); // If jQuery is absolutely necessary

Pattern Migration Rules

Rules for migrating deprecated patterns to modern alternatives.

/**
 * Disallow mixins
 * Promotes composition patterns over mixins
 */
'ember/no-mixins': ESLintRule;

/**
 * Disallow observers
 * Encourages computed properties or autotracking
 */
'ember/no-observers': ESLintRule;

/**
 * Disallow prototype extensions
 * Prevents usage of Ember prototype extensions
 */
'ember/no-function-prototype-extensions': ESLintRule;

Usage Examples:

// ❌ Bad - mixin usage (triggers no-mixins)
import ValidationMixin from '../mixins/validation';

export default Component.extend(ValidationMixin, {
  // component logic
});

// ✅ Good - composition with services or utilities
export default class MyComponent extends Component {
  @service validation;
  
  validate() {
    return this.validation.validateFields(this.fields);
  }
}

// ❌ Bad - observer usage (triggers no-observers)
export default Component.extend({
  userDidChange: observer('user', function() {
    this.updateDisplay();
  })
});

// ✅ Good - computed property or getter
export default class MyComponent extends Component {
  @tracked user;
  
  get displayData() {
    return this.processUser(this.user);
  }
}