or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

blueprint-system.mdcli-commands.mdindex.mdmodels-utilities.mdprogrammatic-api.md
tile.json

blueprint-system.mddocs/

Blueprint System

Ember CLI's blueprint system provides template-based code generation for creating applications, addons, components, and other Ember.js resources. Blueprints are customizable templates that scaffold code with proper file structure, naming conventions, and boilerplate content.

Capabilities

Blueprint Class

The core class for creating and managing blueprints.

class Blueprint {
  /**
   * Extend the Blueprint class to create custom blueprints
   * @param options - Blueprint configuration options
   * @returns Extended Blueprint class
   */
  static extend(options: BlueprintOptions): typeof Blueprint;
  
  /**
   * Look up a blueprint by name
   * @param name - Blueprint name
   * @param options - Lookup options
   * @returns Blueprint instance or null
   */
  static lookup(name: string, options?: LookupOptions): Blueprint | null;
  
  /**
   * List all available blueprints
   * @param options - List options
   * @returns Array of blueprint information
   */
  static list(options?: ListOptions): BlueprintInfo[];
  
  /**
   * Install files from the blueprint
   * @param options - Installation options
   * @returns Promise resolving when installation is complete
   */
  install(options: InstallOptions): Promise<void>;
  
  /**
   * Uninstall files created by the blueprint
   * @param options - Uninstallation options
   * @returns Promise resolving when uninstallation is complete
   */
  uninstall(options: UninstallOptions): Promise<void>;
}

interface BlueprintOptions {
  /** Blueprint name */
  name: string;
  /** Blueprint description */
  description?: string;
  /** Blueprint path */
  path?: string;
  /** Available options */
  availableOptions?: BlueprintOption[];
  /** Anonymous options */
  anonymousOptions?: string[];
  /** Before install hook */
  beforeInstall?: (options: any) => Promise<void>;
  /** After install hook */
  afterInstall?: (options: any) => Promise<void>;
  /** File map function */
  fileMapTokens?: (options: any) => { [key: string]: string };
  /** Locals function for template variables */
  locals?: (options: any) => { [key: string]: any };
}

interface BlueprintOption {
  /** Option name */
  name: string;
  /** Option type */
  type: any;
  /** Default value */
  default?: any;
  /** Option aliases */
  aliases?: string[];
  /** Option description */
  description?: string;
}

interface InstallOptions {
  /** Entity name (e.g., component name) */
  entity: Entity;
  /** UI instance */
  ui: UI;
  /** Project instance */
  project: Project;
  /** Target directory */
  target?: string;
  /** Dry run mode */
  dryRun?: boolean;
  /** Additional options */
  [key: string]: any;
}

interface Entity {
  /** Entity name */
  name: string;
  /** Entity options */
  options: { [key: string]: any };
}

Built-in Blueprints

Ember CLI includes several built-in blueprints for common scaffolding needs.

Application Blueprints

// App blueprint - creates full Ember.js application
// Usage: ember new my-app
interface AppBlueprintOptions {
  /** Skip npm installation */
  skipNpm?: boolean;
  /** Skip git initialization */
  skipGit?: boolean;
  /** Include ember-welcome-page */
  welcome?: boolean;
  /** Package manager to use */
  packageManager?: 'npm' | 'pnpm' | 'yarn';
  /** Base human language */
  lang?: string;
  /** Enable Embroider build system */
  embroider?: boolean;
  /** CI provider configuration */
  ciProvider?: 'github' | 'none';
  /** Include ember-data */
  emberData?: boolean;
}

// Addon blueprint - creates Ember CLI addon
// Usage: ember addon my-addon
interface AddonBlueprintOptions {
  /** Skip npm installation */
  skipNpm?: boolean;
  /** Skip git initialization */
  skipGit?: boolean;
  /** Package manager to use */
  packageManager?: 'npm' | 'pnpm' | 'yarn';
  /** Base human language */
  lang?: string;
  /** CI provider configuration */
  ciProvider?: 'github' | 'none';
}

Development Blueprints

// In-repo-addon blueprint - creates addon within existing app
// Usage: ember generate in-repo-addon my-addon
interface InRepoAddonBlueprintOptions {
  /** Addon name */
  name: string;
}

// Http-mock blueprint - creates mock HTTP server
// Usage: ember generate http-mock api/users
interface HttpMockBlueprintOptions {
  /** Mock endpoint path */
  path: string;
  /** HTTP methods to mock */
  methods?: string[];
}

// Http-proxy blueprint - creates HTTP proxy configuration
// Usage: ember generate http-proxy api
interface HttpProxyBlueprintOptions {
  /** Proxy path */
  path: string;
  /** Target URL */
  target?: string;
}

Component Blueprints

These blueprints are typically used via ember generate command and are extensible by addons.

// Standard blueprints available via ember generate:
// - component: Ember components
// - route: Application routes  
// - controller: Route controllers
// - template: Handlebars templates
// - service: Application services
// - helper: Template helpers
// - model: Ember Data models
// - adapter: Ember Data adapters
// - serializer: Ember Data serializers
// - transform: Ember Data transforms
// - initializer: App initializers
// - instance-initializer: Instance initializers
// - mixin: Ember mixins
// - util: utility functions

Custom Blueprint Creation

Create custom blueprints for project-specific scaffolding needs.

/**
 * Example custom blueprint extending the base Blueprint class
 */
const Blueprint = require('ember-cli/lib/models/blueprint');

module.exports = Blueprint.extend({
  name: 'my-custom-blueprint',
  description: 'Generates a custom component with service injection',
  
  availableOptions: [
    { name: 'service', type: String, description: 'Service to inject' },
    { name: 'skip-test', type: Boolean, default: false }
  ],
  
  anonymousOptions: ['name'],
  
  /**
   * Define template variables
   * @param options - Blueprint options
   * @returns Template locals object
   */
  locals(options) {
    return {
      componentName: options.entity.name,
      serviceName: options.service || 'store',
      className: this.classifyEntityName(options.entity.name)
    };
  },
  
  /**
   * Hook called before installation
   * @param options - Installation options
   */
  async beforeInstall(options) {
    // Validate options or perform setup
    if (options.service && !this.serviceExists(options.service)) {
      throw new Error(`Service ${options.service} does not exist`);
    }
  },
  
  /**
   * Hook called after installation
   * @param options - Installation options
   */
  async afterInstall(options) {
    this.ui.writeLine(`βœ… Generated ${options.entity.name} component`);
    
    if (options.service) {
      this.ui.writeLine(`πŸ”§ Configured with ${options.service} service`);
    }
  }
});

Blueprint File Structure

Blueprints follow a specific directory structure for organizing templates and configuration.

// Blueprint directory structure:
// blueprints/
// └── my-blueprint/
//     β”œβ”€β”€ index.js              # Blueprint configuration
//     └── files/                # Template files
//         β”œβ”€β”€ app/
//         β”‚   └── __path__/
//         β”‚       └── __name__.js
//         └── tests/
//             └── unit/
//                 └── __path__/
//                     └── __name__-test.js

interface BlueprintFileStructure {
  /** Blueprint configuration file */
  'index.js': string;
  /** Template files directory */
  files: {
    /** File path with tokens */
    [path: string]: string;
  };
}

// File path tokens:
// __name__         # Entity name
// __path__         # Entity path
// __root__         # Project root
// __test__         # Test file suffix

File Map Tokens

Customize how blueprint file paths are resolved using token replacement.

/**
 * Define custom file path tokens
 * @param options - Blueprint options
 * @returns Token replacement map
 */
fileMapTokens(options) {
  return {
    __name__: options.entity.name,
    __path__: options.entity.name.replace(/\//g, path.sep),
    __component__: `components/${options.entity.name}`,
    __service__: `services/${options.serviceName || 'store'}`
  };
}

Template Processing

Blueprint files support template syntax for dynamic content generation.

// Template file example (files/app/components/__name__.js):
import Component from '@glimmer/component';
<% if (serviceName) { %>import { inject as service } from '@ember/service';<% } %>

export default class <%= className %>Component extends Component {
  <% if (serviceName) { %>@service <%= serviceName %>;<% } %>
  
  // Component implementation
}

// Test template (files/tests/unit/components/__name__-test.js):
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';

module('<%= classifyEntityName(name) %>', function(hooks) {
  setupTest(hooks);
  
  test('it exists', function(assert) {
    // Test implementation
    assert.ok(true);
  });
});

Blueprint Utilities

Utility functions available within blueprint contexts for string manipulation and path handling.

interface BlueprintUtils {
  /**
   * Convert string to camelCase
   * @param str - Input string
   * @returns camelCase string
   */
  camelizeEntityName(str: string): string;
  
  /**
   * Convert string to PascalCase
   * @param str - Input string  
   * @returns PascalCase string
   */
  classifyEntityName(str: string): string;
  
  /**
   * Convert string to dasherized format
   * @param str - Input string
   * @returns dasherized-string
   */
  dasherizeEntityName(str: string): string;
  
  /**
   * Get entity path for file placement
   * @param entity - Entity object
   * @returns File path string
   */
  pathForEntity(entity: Entity): string;
}

Usage Examples

Using Built-in Blueprints

# Generate component
ember generate component user-profile

# Generate component with options
ember generate component navigation --pod

# Generate service
ember generate service auth

# Generate route with model
ember generate route users

# Generate addon
ember addon my-awesome-addon

# Generate in-repo addon
ember generate in-repo-addon shared-components

Creating Custom Blueprint

// blueprints/feature/index.js
const Blueprint = require('ember-cli/lib/models/blueprint');

module.exports = Blueprint.extend({
  name: 'feature',
  description: 'Generates a complete feature with component, service, and route',
  
  availableOptions: [
    { name: 'skip-route', type: Boolean, default: false },
    { name: 'skip-service', type: Boolean, default: false }
  ],
  
  async afterInstall(options) {
    const entityName = options.entity.name;
    
    // Generate component
    await this.addBlueprintToProject('component', entityName);
    
    // Generate service unless skipped
    if (!options.skipService) {
      await this.addBlueprintToProject('service', entityName);
    }
    
    // Generate route unless skipped
    if (!options.skipRoute) {
      await this.addBlueprintToProject('route', entityName);
    }
    
    this.ui.writeLine(`βœ… Generated complete ${entityName} feature`);
  }
});

Programmatic Blueprint Usage

const Blueprint = require('ember-cli/lib/models/blueprint');
const Project = require('ember-cli/lib/models/project');

async function generateComponent(name, options = {}) {
  const project = Project.closestSync(process.cwd());
  const blueprint = Blueprint.lookup('component');
  
  if (!blueprint) {
    throw new Error('Component blueprint not found');
  }
  
  await blueprint.install({
    entity: { name, options },
    project,
    ui: project.ui
  });
}

// Generate component programmatically
generateComponent('user-card', { pod: true })
  .then(() => console.log('Component generated'))
  .catch(console.error);

Blueprint Discovery

const Blueprint = require('ember-cli/lib/models/blueprint');

// List all available blueprints
const blueprints = Blueprint.list({
  paths: [project.root, ...project.addonPackages]
});

console.log('Available blueprints:');
blueprints.forEach(bp => {
  console.log(`- ${bp.name}: ${bp.description}`);
});

// Find specific blueprint
const componentBlueprint = Blueprint.lookup('component', {
  paths: [project.root]
});

if (componentBlueprint) {
  console.log('Component blueprint found at:', componentBlueprint.path);
}

Advanced Blueprint Features

Conditional File Generation

// Blueprint with conditional files
module.exports = Blueprint.extend({
  name: 'conditional-component',
  
  locals(options) {
    return {
      includeService: options.service,
      includeTest: !options.skipTest
    };
  },
  
  files() {
    let files = ['app/components/__name__.js'];
    
    if (this.options.includeService) {
      files.push('app/services/__name__.js');
    }
    
    if (this.options.includeTest) {
      files.push('tests/unit/components/__name__-test.js');
    }
    
    return files;
  }
});

Blueprint Inheritance

// Extend existing blueprint
const ComponentBlueprint = require('ember-cli/blueprints/component');

module.exports = ComponentBlueprint.extend({
  name: 'enhanced-component',
  
  locals(options) {
    const parentLocals = this._super.locals.call(this, options);
    
    return Object.assign(parentLocals, {
      includeTracking: true,
      componentType: 'enhanced'
    });
  }
});

Blueprint Testing

// Test blueprint functionality
const { setupTest } = require('ember-cli-blueprint-test-helpers');
const { expect } = require('chai');

describe('my-blueprint', function() {
  setupTest();
  
  it('generates expected files', async function() {
    await this.generate(['my-blueprint', 'foo']);
    
    expect(file('app/foo.js')).to.exist;
    expect(file('tests/unit/foo-test.js')).to.exist;
  });
  
  it('supports options', async function() {
    await this.generate(['my-blueprint', 'foo', '--skip-test']);
    
    expect(file('app/foo.js')).to.exist;
    expect(file('tests/unit/foo-test.js')).to.not.exist;
  });
});