4.7 KiB
title, impact, impactDescription, tags
| title | impact | impactDescription | tags |
|---|---|---|---|
| Prefer Named Exports, Fallback to Default for Implicit Template Lookup | LOW | Clear export contracts across .hbs and template-tag codebases | exports, hbs, gjs, interop, code-organization |
Prefer Named Exports, Fallback to Default for Implicit Template Lookup
Use named exports for shared modules imported directly in JS/TS (utilities, constants, pure functions). If a module should be invokable from .hbs templates via implicit lookup, provide a default export. In hybrid .gjs/.hbs projects, a practical pattern is a named export plus a default export alias.
Incorrect (default export in a shared utility module):
// app/utils/format-date.js
export default function formatDate(date) {
return new Date(date).toLocaleDateString();
}
Correct (named export in a shared utility module):
// app/utils/format-date.js
export function formatDate(date) {
return new Date(date).toLocaleDateString();
}
Correct (hybrid .gjs/.hbs named export + default alias):
// app/helpers/format-date.js
import { helper } from '@ember/component/helper';
export const formatDate = helper(([value]) => {
return new Date(value).toLocaleDateString();
});
export default formatDate;
Where Named Exports Are Preferred
Use named exports when the module is imported directly by other modules and is not resolved via implicit template lookup.
Example (utility module with multiple named exports):
// app/utils/validators.js
export function isEmail(value) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
}
export function isPhoneNumber(value) {
return /^\d{3}-\d{3}-\d{4}$/.test(value);
}
Benefits:
- Explicit import contracts
- Better refactor safety (symbol rename tracking)
- Better tree-shaking for utility modules
- Easier multi-export module organization
Where Default Exports Are Required
Use default exports for modules consumed through resolver/template lookup.
If your project uses .hbs, invokables that should be accessible from templates should provide export default.
In hybrid .gjs/.hbs codebases, use named exports plus a default export alias where you want both explicit imports and template compatibility.
Service:
// app/services/auth.js
import Service from '@ember/service';
export default class AuthService extends Service {
// ...
}
Route:
// app/routes/dashboard.js
import Route from '@ember/routing/route';
import { service } from '@ember/service';
export default class DashboardRoute extends Route {
@service store;
model() {
return this.store.findAll('dashboard-item');
}
}
Modifier (when invoked from .hbs):
// app/modifiers/focus.js
import { modifier } from 'ember-modifier';
export default modifier((element) => {
element.focus();
});
Template (.gjs):
// app/templates/dashboard.gjs
<template>
<h1>Dashboard</h1>
</template>
Template (.gts):
// app/templates/dashboard.gts
import type { TOC } from '@ember/component/template-only';
interface Signature {
Args: {
model: unknown;
};
}
export default <template>
<h1>Dashboard</h1>
</template> satisfies TOC<Signature>;
Template-tag files must resolve via a module default export in convention-based and import.meta.glob flows.
For app/templates/*.gjs, the default export is implicit after compilation.
Strict Resolver Nuance
With ember-strict-application-resolver, you can register explicit module values in App.modules:
Strict resolver explicit modules registration:
modules = {
'./services/manual': { default: ManualService },
'./services/manual-shorthand': ManualService,
};
In that explicit shorthand case, a direct value works without a default-exported module object.
This is an explicit registration escape hatch and does not replace default-export requirements for .hbs-invokable modules.
Rule of Thumb
- If a module should be invokable from
.hbs, provide a default export. - In hybrid
.gjs/.hbsprojects, use named export + default alias for resolver-facing modules. - Strict resolver explicit
modulesentries may use direct shorthand values where appropriate. - Plain shared modules (
app/utils, shared constants, reusable pure functions): prefer named exports. - Template-tag components (
.gjs/.gts): follow the component file-conventions rule and use named class exports.