Ember.js addon providing command-line tools and build-time utilities for managing optional features in ember-source applications
npx @tessl/cli install tessl/npm-ember--optional-features@2.2.0@ember/optional-features is an Ember.js addon that provides command-line tools and build-time utilities for managing optional features in ember-source applications. It enables developers to easily toggle opt-in/opt-out features that remain optional for the foreseeable future, with automated codemods and feature validation.
ember install @ember/optional-featuresFor build-time usage (in addon's index.js):
// The addon is automatically available through Ember's addon system
let optionalFeatures = this.addons.find(a => a.name === '@ember/optional-features');For utility functions:
const { getConfigPath, join, strip } = require('@ember/optional-features/utils');# List all available features
ember feature:list
# Enable a feature
ember feature:enable template-only-glimmer-components
# Disable a feature
ember feature:disable jquery-integration
# Enable with automatic codemod execution
ember feature:enable application-template-wrapper --run-codemod// In an addon's index.js
included() {
let optionalFeatures = this.addons.find(a => a.name === '@ember/optional-features');
if (optionalFeatures.isFeatureEnabled('jquery-integration')) {
// Include jQuery-specific functionality
}
if (optionalFeatures.isFeatureExplicitlySet('template-only-glimmer-components')) {
// Handle explicit feature configuration
}
}@ember/optional-features is built around several key components:
Interactive Ember CLI commands for managing optional features from the terminal. Includes feature listing, enabling/disabling with validation, and optional automated codemods.
// Commands exported by includedCommands()
interface Commands {
'feature': Command; // Shows usage information
'feature:list': Command; // Lists available features
'feature:enable': Command; // Enables a specific feature
'feature:disable': Command; // Disables a specific feature
}
interface Command {
name: string;
description: string;
works: string;
run: (commandOptions?: any, args?: string[]) => Promise<void> | void;
}Build-time methods for addons to conditionally include functionality based on feature states. Provides feature checking and explicit configuration detection.
// Main addon methods available to other addons
interface AddonMethods {
isFeatureEnabled(name: string): boolean;
isFeatureExplicitlySet(name: string): boolean;
config(): { EmberENV: Record<string, any> };
}Modular feature definitions with metadata, version requirements, and optional callback functions for codemods. Features are automatically discovered from the features directory.
interface FeatureDefinition {
description: string; // Human readable description
url: string; // RFC or documentation URL
default: boolean; // Default enabled state
since: string; // Minimum Ember version (e.g. "3.1.0")
callback?: (project: any, value: boolean, shouldRunCodemod?: boolean) => Promise<void>;
}
// Available features (frozen object)
const FEATURES: Readonly<Record<string, Readonly<FeatureDefinition>>>;Helper functions for configuration path resolution and template string processing. Used internally by the addon and available for external use.
interface UtilityFunctions {
getConfigPath(project: any): string;
join(strings: TemplateStringsArray, ...args: any[]): string;
strip(strings: TemplateStringsArray, ...args: any[]): string;
}The addon includes the following predefined optional features:
<div class="ember-view">Features are configured in config/optional-features.json:
{
"template-only-glimmer-components": true,
"jquery-integration": false
}Configuration can be overridden via the EMBER_OPTIONAL_FEATURES environment variable:
EMBER_OPTIONAL_FEATURES='{"jquery-integration": false}' ember build// Project interface used throughout the addon
interface EmberProject {
root: string;
pkg: any;
require(path: string): any;
resolveSync(path: string): string;
config(): any;
}
// Configuration structure
interface FeatureConfig {
[featureName: string]: boolean | null | undefined;
}