Zero-config import from NPM packages for Ember applications and addons
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Internal API for managing import analysis, package resolution, and bundle generation. These APIs are primarily used internally by ember-auto-import but are exposed for advanced use cases and addon coordination.
Core class managing auto-import functionality and leader election between multiple addon instances:
/**
* Core AutoImport class managing import analysis and bundling
* Implements leader election pattern for multi-addon coordination
*/
class AutoImport implements AutoImportSharedAPI {
/** Register an addon instance with leader election system */
static register(addon: AddonInstance): void;
/** Lookup the leader instance for an addon */
static lookup(addon: AddonInstance): AutoImportSharedAPI;
/** Create new AutoImport instance for an addon */
constructor(addonInstance: AddonInstance);
/** Install app filter for allowAppImports patterns */
installAppFilter(host: AppInstance): void;
/** Check if addon is primary (legacy, always returns false) */
isPrimary(addon: AddonInstance): boolean;
/** Analyze tree for import statements */
analyze(tree: Node, addon: AddonInstance, treeType?: TreeType, supportsFastAnalyzer?: true): Analyzer;
/** Register v2 addon for Embroider compatibility */
registerV2Addon(packageName: string, packageRoot: string, options?: CompatOptions): void;
/** Get v2 addon resolver interface */
get v2AddonResolver(): V2AddonResolver;
/** Add auto-import bundles to build tree */
addTo(allAppTree: Node): Node;
/** Handle addon inclusion in build process */
included(addonInstance: AddonInstance): void;
}Usage Example:
// Internal usage by ember-auto-import addon
AutoImport.register(this); // Register addon instance
// Lookup leader instance
const leader = AutoImport.lookup(this);
const analyzer = leader.analyze(tree, this, 'app');Stable API interface for cross-version compatibility between different ember-auto-import versions:
/**
* Shared API interface for cross-version compatibility
* Ensures consistent behavior across different addon versions
*/
interface AutoImportSharedAPI {
/** Check if addon instance is primary (legacy method) */
isPrimary(addonInstance: AddonInstance): boolean;
/**
* Analyze tree for import statements and return analyzer
* Creates new Analyzer instance for the package
*/
analyze(
tree: Node,
addon: AddonInstance,
treeType?: TreeType,
supportsFastAnalyzer?: true
): Node;
/** Handle addon inclusion, install babel plugin and configure build */
included(addonInstance: AddonInstance): void;
/** Add webpack bundles to the final build tree */
addTo(tree: Node): Node;
/** Register v2 addon with optional compatibility options */
registerV2Addon(
packageName: string,
packageRoot: string,
compatOptions?: CompatOptions
): void;
}Manages individual Ember packages/addons and their auto-import configuration:
/**
* Represents an Ember package/addon with auto-import configuration
* Handles package resolution, babel config, and import categorization
*/
class Package {
/** Package name */
name: string;
/** Package root directory */
root: string;
/** Package root directory (may differ from root for dummy apps) */
pkgRoot: string;
/** Whether this package is an addon */
isAddon: boolean;
/** Whether this package is in development mode */
isDeveloping: boolean;
/** Package cache object */
pkgCache: any;
/** Package generation for cache invalidation */
pkgGeneration: number;
/** Create new Package instance for addon child */
constructor(child: AddonInstance, extraResolve: V2AddonResolver);
/** Lookup parent package of a child addon */
static lookupParentOf(child: AddonInstance, extraResolve: V2AddonResolver): Package;
/** Categorize import path type */
static categorize(importedPath: string, partial?: boolean): "local" | "url" | "imprecise" | "dep";
/** Check if package has a dependency */
hasDependency(name: string): boolean;
/** Get requested version range for a package */
requestedRange(packageName: string): string | undefined;
/** Resolve import path to package or local file */
resolve(importedPath: string, fromPath: string): DepResolution | LocalResolution | URLResolution | undefined;
/** Resolve with partial matching support */
resolve(importedPath: string, fromPath: string, partial: true): Resolution | undefined;
/** Get public asset URL for the package */
publicAssetURL(): string;
/** Get browserslist configuration */
browserslist(): string;
/** Get clean babel configuration without ember-specific settings */
cleanBabelConfig(): TransformOptions;
/** Get babel options for the package */
get babelOptions(): TransformOptions;
/** Get babel major version */
get babelMajorVersion(): number | undefined;
/** Check if FastBoot is enabled */
get isFastBootEnabled(): boolean;
/** Get webpack configuration */
get webpackConfig(): any;
/** Get packages to skip babel transformation */
get skipBabel(): Options['skipBabel'];
/** Get import aliases */
get aliases(): Record<string, string> | undefined;
/** Get supported file extensions */
get fileExtensions(): string[];
/** Get style loader options */
get styleLoaderOptions(): Record<string, unknown> | undefined;
/** Get CSS loader options */
get cssLoaderOptions(): Record<string, unknown> | undefined;
/** Get mini CSS extract plugin options */
get miniCssExtractPluginOptions(): Record<string, unknown> | undefined;
/** Check if eval is forbidden */
get forbidsEval(): boolean;
/** Get custom script insertion location */
get insertScriptsAt(): string | undefined;
/** Get custom style insertion location */
get insertStylesAt(): string | undefined;
/** Get directories to watch for changes */
get watchedDirectories(): string[] | undefined;
/** Get app import patterns to handle */
get allowAppImports(): string[];
}Usage Examples:
// Package resolution and categorization
const category = Package.categorize('lodash'); // 'dep'
const category2 = Package.categorize('./utils'); // 'local'
const category3 = Package.categorize('https://cdn.com/lib.js'); // 'url'
// Package lookup and dependency checking
const pkg = Package.lookupParentOf(addonInstance, v2Resolver);
const hasLodash = pkg.hasDependency('lodash');
const lodashVersion = pkg.requestedRange('lodash'); // '^4.17.0'Analyzes JavaScript/TypeScript files for import statements and categorizes them:
/**
* Analyzes source files for import statements
* Extends Broccoli Funnel for file processing
*/
class Analyzer extends Funnel {
/** All discovered imports from analyzed files */
get imports(): Import[];
/** Build and analyze files for imports */
build(...args: unknown[]): Promise<void>;
/** Remove imports from a specific file */
removeImports(relativePath: string): void;
/** Update imports from a specific file */
updateImports(relativePath: string): Promise<void>;
/** Get parser function for import analysis */
parser(): Promise<(source: string, relativePath: string) => ImportSyntax[]>;
}Support for Embroider v2 addons with renaming and compatibility options:
/**
* V2 addon resolver interface for Embroider compatibility
* Handles module renaming and v2 addon registration
*/
interface V2AddonResolver {
/** Check if a v2 addon is registered */
hasV2Addon(name: string): boolean;
/** Get root directory of a v2 addon */
v2AddonRoot(name: string): string | undefined;
/** Handle module renaming for v2 addons */
handleRenaming(name: string): string;
}
/**
* Compatibility options for v2 addon registration
* Allows customization of addon metadata
*/
interface CompatOptions {
/** Hook to customize addon metadata before processing */
customizeMeta?: (meta: AddonMeta) => AddonMeta;
}Usage Example:
// Register v2 addon with custom metadata handling
autoImport.registerV2Addon('my-v2-addon', '/path/to/addon', {
customizeMeta(meta) {
// Customize how the addon metadata is processed
return {
...meta,
'renamed-modules': {
'old-name': 'new-name'
}
};
}
});Different types of import resolution results:
/**
* NPM package dependency resolution
*/
interface DepResolution {
type: 'package';
packageName: string;
packageRoot: string;
resolvedSpecifier: string;
}
/**
* Local file resolution
*/
interface LocalResolution {
type: 'local';
local: string;
}
/**
* URL resolution for external resources
*/
interface URLResolution {
type: 'url';
url: string;
}
/**
* Imprecise resolution when path cannot be fully resolved
*/
interface ImpreciseResolution {
type: 'imprecise';
}
/**
* Union type for all resolution results
*/
type Resolution = DepResolution | LocalResolution | URLResolution | ImpreciseResolution;Different types of import statements discovered during analysis:
/**
* Static string import statement with package context
*/
interface LiteralImport {
path: string;
package: Package;
treeType: TreeType | undefined;
// Additional import syntax properties
}
/**
* Template literal import statement with package context
*/
interface TemplateImport {
path: string;
package: Package;
treeType: TreeType | undefined;
// Template literal specific properties
}
/**
* Union type for all import types
*/
type Import = LiteralImport | TemplateImport;
/**
* Tree type indicating which part of the Ember app the import is from
*/
type TreeType = 'app' | 'addon' | 'addon-templates' | 'addon-test-support' | 'styles' | 'templates' | 'test';
/**
* Base interface for import syntax discovered during analysis
*/
interface ImportSyntax {
/** File path where the import was found */
path: string;
/** Whether this is a dynamic import() call */
isDynamic: boolean;
}
/**
* String literal import syntax (static imports and simple dynamic imports)
*/
interface LiteralImportSyntax extends ImportSyntax {
/** The imported module specifier */
specifier: string;
}
/**
* Template literal import syntax (dynamic imports with template strings)
*/
interface TemplateImportSyntax extends ImportSyntax {
/** Cooked template literal parts */
cookedQuasis: string[];
/** Expression name hints for template variables */
expressionNameHints: string[];
}Utility functions for package management and analysis:
/**
* Reload dev packages cache
* Used during development to refresh package information
* Increments global package generation counter to invalidate caches
*/
function reloadDevPackages(): void;Usage Example:
// Force refresh of package cache during development
// Useful when package.json or dependencies change during development
reloadDevPackages();For addon authors who need to integrate with ember-auto-import:
// In your addon's index.js
module.exports = {
name: 'my-addon',
init() {
this._super.init.apply(this, arguments);
// Register with ember-auto-import if present
if (this.project.findAddonByName('ember-auto-import')) {
AutoImport.register(this);
}
},
included() {
this._super.included.apply(this, arguments);
// Get leader instance and configure
const autoImport = AutoImport.lookup(this);
if (autoImport) {
autoImport.included(this);
}
}
};Access import analysis results for build-time processing:
// Access discovered imports after analysis
const analyzer = autoImport.analyze(tree, this, 'app');
// analyzer.imports contains all discovered imports
analyzer.imports.forEach(importItem => {
console.log(`Found import: ${importItem.path} in ${importItem.package.name}`);
});Install with Tessl CLI
npx tessl i tessl/npm-ember-auto-import