CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-parcel--plugin

Plugin API for Parcel bundler - provides base classes for creating Parcel plugins including transformers, resolvers, bundlers, namers, runtimes, packagers, optimizers, compressors, reporters, and validators

67

1.06x
Overview
Eval results
Files

naming.mddocs/

Bundle Naming

The Namer plugin generates output filenames for bundles based on content, configuration, and optimization strategies. Namers control how bundles are named in the final build output.

Capabilities

Namer Class

Base class for creating bundle naming plugins.

/**
 * Base class for bundle naming plugins
 * @template T - Configuration type for this namer
 */
export declare class Namer<T> {
  constructor(opts: NamerOpts<T>);
}

/**
 * Namer plugin configuration interface
 * @template ConfigType - Type of configuration returned by loadConfig
 */
interface NamerOpts<ConfigType> {
  /** Load configuration for this namer */
  loadConfig?: (args: {
    config: Config;
    options: PluginOptions;
    logger: PluginLogger;
    tracer: PluginTracer;
  }) => Promise<ConfigType> | ConfigType;

  /** Generate a filename for a bundle (required) */
  name(args: {
    bundle: Bundle;
    bundleGraph: BundleGraph<Bundle>;
    config: ConfigType;
    options: PluginOptions;
    logger: PluginLogger;
    tracer: PluginTracer;
  }): Promise<FilePath | null>;
}

Usage Example:

import { Namer } from "@parcel/plugin";
import path from "path";
import crypto from "crypto";

export default new Namer({
  // Load namer configuration
  loadConfig({config}) {
    return {
      pattern: config.pattern || '[name].[hash].[ext]',
      hashLength: config.hashLength || 8,
      preserveEntryNames: config.preserveEntryNames !== false
    };
  },

  // Generate bundle filename (required)
  async name({bundle, bundleGraph, config, options}) {
    const bundleAssets = bundleGraph.getBundleAssets(bundle);
    
    // Use entry name for entry bundles
    if (bundle.entryAsset && config.preserveEntryNames) {
      const entryName = path.basename(
        bundle.entryAsset.filePath, 
        path.extname(bundle.entryAsset.filePath)
      );
      
      return this.formatName(config.pattern, {
        name: entryName,
        hash: this.generateHash(bundleAssets),
        ext: this.getExtension(bundle.type)
      });
    }
    
    // Generate name based on content
    const contentHash = this.generateHash(bundleAssets);
    const baseName = this.generateBaseName(bundle, bundleAssets);
    
    return this.formatName(config.pattern, {
      name: baseName,
      hash: contentHash.slice(0, config.hashLength),
      ext: this.getExtension(bundle.type)
    });
  },

  // Helper methods
  generateHash(assets) {
    const hasher = crypto.createHash('md5');
    for (const asset of assets) {
      hasher.update(asset.getCode());
    }
    return hasher.digest('hex');
  },

  generateBaseName(bundle, assets) {
    if (assets.length === 1) {
      return path.basename(assets[0].filePath, path.extname(assets[0].filePath));
    }
    return 'chunk';
  },

  formatName(pattern, vars) {
    return pattern.replace(/\[(\w+)\]/g, (match, key) => vars[key] || match);
  },

  getExtension(bundleType) {
    const extensions = {
      'js': 'js',
      'css': 'css',
      'html': 'html',
      'json': 'json'
    };
    return extensions[bundleType] || bundleType;
  }
});

Bundle Information for Naming

/**
 * Bundle information available for naming
 */
interface Bundle {
  /** Bundle ID */
  id: string;
  
  /** Bundle type (js, css, html, etc.) */
  type: string;
  
  /** Entry asset for entry bundles */
  entryAsset?: Asset;
  
  /** Main entry asset */
  mainEntryAsset?: Asset;
  
  /** Target environment */
  target: Target;
  
  /** Whether this bundle needs a stable name */
  needsStableName: boolean;
  
  /** Bundle behavior */
  bundleBehavior?: BundleBehavior;
  
  /** Bundle display name */
  displayName?: string;
  
  /** Bundle metadata */
  meta: Record<string, any>;
}

/**
 * Bundle graph for accessing related bundles and assets
 */
interface BundleGraph<TBundle> {
  /** Get assets in a bundle */
  getBundleAssets(bundle: TBundle): Array<Asset>;
  
  /** Get bundle dependencies */
  getBundleDependencies(bundle: TBundle): Array<TBundle>;
  
  /** Get bundles that depend on this bundle */
  getBundleDependents(bundle: TBundle): Array<TBundle>;
  
  /** Get all bundles */
  getBundles(): Array<TBundle>;
  
  /** Check if bundle has dependency on another bundle */
  bundleHasDependency(bundle: TBundle, dependency: TBundle): boolean;
}

Common Naming Patterns

Hash-based Naming:

// Content-based hashing for cache busting
const contentHash = crypto.createHash('md5');
for (const asset of bundleAssets) {
  contentHash.update(asset.getCode());
}
const hash = contentHash.digest('hex').slice(0, 8);

return `${baseName}.${hash}.${extension}`;

Entry Name Preservation:

// Preserve original entry file names
if (bundle.entryAsset && bundle.needsStableName) {
  const originalName = path.basename(
    bundle.entryAsset.filePath,
    path.extname(bundle.entryAsset.filePath)
  );
  return `${originalName}.${extension}`;
}

Hierarchical Naming:

// Create directory structure based on bundle type
const typeDir = bundle.type;
const targetDir = bundle.target.name;

return path.join(targetDir, typeDir, `${baseName}.${extension}`);

Chunk Naming:

// Name shared chunks based on their dependencies
const dependencies = bundleGraph.getBundleDependencies(bundle);
if (dependencies.length > 1) {
  const depNames = dependencies
    .map(dep => dep.displayName || 'chunk')
    .sort()
    .join('-');
  return `shared-${depNames}.${hash}.${extension}`;
}

File Extensions by Bundle Type

/**
 * Common file extensions for different bundle types
 */
interface BundleTypeExtensions {
  'js': 'js';
  'css': 'css';
  'html': 'html';
  'json': 'json';
  'xml': 'xml';
  'txt': 'txt';
  'wasm': 'wasm';
  'webmanifest': 'json';
}

Naming Configuration Options

/**
 * Common naming configuration options
 */
interface NamingConfig {
  /** Filename pattern with placeholders */
  pattern?: string;
  
  /** Length of content hash */
  hashLength?: number;
  
  /** Whether to preserve entry file names */
  preserveEntryNames?: boolean;
  
  /** Output subdirectories by type */
  outputDir?: Record<string, string>;
  
  /** Custom name mapping */
  customNames?: Record<string, string>;
}

Pattern Placeholders:

  • [name] - Base name of the file or bundle
  • [hash] - Content hash for cache busting
  • [ext] - File extension based on bundle type
  • [id] - Bundle ID
  • [type] - Bundle type
  • [target] - Target environment name

Install with Tessl CLI

npx tessl i tessl/npm-parcel--plugin

docs

bundling.md

compression.md

index.md

naming.md

optimization.md

packaging.md

reporting.md

resolution.md

runtime.md

transformation.md

validation.md

tile.json