Use when writing JavaScript for Discourse core, themes, or plugins - covers jQuery phaseout, Ember patterns, singleton imports, lifecycle hooks, and cleanup requirements
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Une commande directe contourne le prompt de vérification. Examinez la source avant de l'exécuter.
Use when writing JavaScript for Discourse core, themes, or plugins - covers jQuery phaseout, Ember patterns, singleton imports, lifecycle hooks, and cleanup requirements
Discourse JavaScript Patterns
Overview
Required patterns for JavaScript in Discourse (Ember-based application). These patterns are prescriptive - don't follow every pattern you see in existing code. Follow these rules instead.
Critical: Don't Follow All Existing Patterns
Just because code exists in the codebase doesn't mean you should copy it.
Discourse is actively modernizing JavaScript. Old patterns remain while being phased out. When you see conflicting patterns, follow this skill's guidance.
Never Use jQuery
Rule: Never use jQuery in new code. Use native DOM methods instead.
Why: Browsers now support jQuery's functionality natively. jQuery is being phased out.
Rule: Use dependency injection, not singleton imports.
Why: Singletons bypass Ember's container and make testing difficult.
// ❌ NEVER - Singleton importimportSitefrom"discourse/models/site";
console.log(Site.currentProp("top_menu_items"));
// ✅ ALWAYS - Dependency injection// In components, routes, controllers - site is auto-injected:console.log(this.site.top_menu_items);
// In initializers:let site = container.lookup("site:main");
console.log(site.top_menu_items);
The site model is automatically injected into components, routes, and controllers. Just use this.site.
Always store references to listeners and timer IDs so you can clean them up.
Never Use Default Objects or Arrays
Rule: Initialize objects and arrays in init(), not as default values.
Why: Default objects/arrays are shared across all instances (shared reference).
// ❌ NEVER - Default objects/arraysexportdefaultEmberObject.extend({
items: [], // SHARED REFERENCEconfig: {} // SHARED REFERENCE
});
// ✅ ALWAYS - Initialize in init() (EmberObject)exportdefaultEmberObject.extend({
items: null,
config: null,
init() {
this._super(...arguments);
this.items = [];
this.config = {};
}
});
// ✅ OR - Use native classes (no issue)exportdefaultclass {
items = []; // Each instance gets own array
config = {}; // Each instance gets own object
}
Private Fields: Use # Syntax
Rule: Make fields truly private with # unless decorator needed or used in template.
exportdefaultclassextendsComponent {
// Public - used in template
@tracked count = 0;
// Public - decorator doesn't work on private
@action
increment() {
this.#updateCount();
}
// Private - not used in template
#internalState = null;
#updateCount() {
this.count++;
}
}
Don't use underscore prefix (_field) - that's the old way. Use # for true privacy.
Avoid Array Prototype Extensions
Rule: Use native Array methods, not Ember's prototype extensions.
For arrays that need reactivity, use TrackedArray instead of native array.
Avoid Observers
Rule: Use action handlers for user events. Use native getters for derived data.
Why: From Ember docs: "Observers are often over-used by new Ember developers. Most of the time, you will be observing an action the user took, such as clicking a button. Instead of an observer, consider putting that code in the action handler itself."
// ❌ NEVER - Observer
@observes('userInput')
inputChanged() {
this.processInput();
}
// ✅ ALWAYS - Action handler for user events
<input {{on "input"this.processInput}} />
When you need derived data (not side effects), use native getters:
// ✅ Native getter for computed valuesgetfullName() {
return`${this.firstName}${this.lastName}`;
}
Don't use getters for side effects - use action handlers instead.
Common Mistakes
Mistake
Fix
Using jQuery because it exists
Use native DOM methods - jQuery is being phased out
Importing singletons "just to check"
Use dependency injection - this.site, this.siteSettings, etc.
"self = this for backward compatibility"
Arrow functions are widely supported - use them
Following @on pattern from old code
Use explicit lifecycle methods - better for Glimmer
Skipping cleanup "it's just a demo"
Always clean up - prevents real bugs in production
underscore prefix for "private"
Use # for true private fields
Rationalization Red Flags
These thoughts mean STOP - check this skill:
Rationalization
Reality
"jQuery is already in the codebase"
Being present ≠ permission to use. Use native methods.
"Need backward compatibility"
Arrow functions are ES6 (2015). Widely supported. Use them.
"Matching codebase style I saw"
Not all existing patterns should be copied. Follow this skill.
"Standard pattern in older JavaScript"
Modern alternatives exist. Don't use old patterns.
"This code works fine"
Working ≠ best practice. Follow current patterns.
"It's technically correct"
Technically correct but wrong approach. Check alternatives.
All of these mean: Review this skill and use current patterns.