or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

ast-operations.mdbrowser-integration.mdindex.mdplugin-system.mdtransformation.mdutilities.md
tile.json

plugin-system.mddocs/

Plugin System

Comprehensive plugin loading, configuration management, and preset system for extensible JavaScript transformations with Babel's plugin architecture.

Capabilities

Plugin Resolution

Functions for resolving and loading plugins by name or path.

/**
 * Resolve plugin by name or path
 * @param {string} name - Plugin name or path to resolve
 * @returns {Object} Resolved plugin object
 */
function resolvePlugin(name);

/**
 * Resolve preset by name or path  
 * @param {string} name - Preset name or path to resolve
 * @returns {Object} Resolved preset object
 */
function resolvePreset(name);

Usage Examples:

const babel = require("babel-core");

// Resolve plugin by name
const arrowPlugin = babel.resolvePlugin("transform-es2015-arrow-functions");
console.log(arrowPlugin); // Plugin object

// Resolve preset by name  
const es2015Preset = babel.resolvePreset("es2015");
console.log(es2015Preset); // Preset configuration

// Use resolved plugins in transform
const result = babel.transform("const fn = () => 42;", {
  plugins: [arrowPlugin]
});
console.log(result.code);

Option Manager

Advanced option processing and normalization for complex plugin configurations.

/**
 * Option Manager for processing transformation options
 */
class OptionManager {
  /**
   * Create new OptionManager instance
   * @param {Logger} log - Logger instance (optional)
   * @param {Pipeline} pipeline - Pipeline instance (optional)
   */
  constructor(log, pipeline);
  
  /**
   * Initialize and normalize options
   * @param {Object} opts - Raw options object
   * @returns {Object} Normalized options
   */
  init(opts);
  
  /**
   * Create bare options object with defaults
   * @returns {Object} Default options object
   */
  static createBareOptions();
  
  /**
   * Normalize plugin object
   * @param {any} plugin - Plugin to normalize
   * @param {string} loc - Plugin location for errors
   * @param {number} i - Plugin index for errors
   * @param {string} alias - Plugin alias
   * @returns {Plugin} Normalized plugin instance
   */
  static normalisePlugin(plugin, loc, i, alias);
}

// Access OptionManager
const OptionManager = babel.OptionManager;

Usage Examples:

const babel = require("babel-core");

// Create option manager
const OptionManager = babel.OptionManager;
const optionManager = new OptionManager();

// Process raw options
const rawOptions = {
  presets: ["es2015"],
  plugins: ["transform-runtime"],
  sourceMaps: true,
  filename: "input.js"
};

const normalizedOptions = optionManager.init(rawOptions);
console.log(normalizedOptions); // Fully processed options

// Create default options
const defaultOptions = OptionManager.createBareOptions();
console.log(defaultOptions); // Default option set

Configuration Schema

Babel Core exports a configuration schema object defining all available options.

/**
 * Configuration schema object defining all available Babel options
 */
const options;

Usage Examples:

const babel = require("babel-core");

// Access configuration schema
console.log(babel.options);  // Configuration schema object

// Schema includes definitions for:
// - filename, filenameRelative, inputSourceMap
// - env, mode, retainLines, highlightCode
// - presets, plugins, babelrc, extends
// - ignore, only, sourceMaps, sourceType
// - parserOpts, generatorOpts, etc.

// Use schema for validation or tool integration
function validateBabelConfig(userConfig) {
  const schema = babel.options;
  
  // Validate user config against schema
  for (const key in userConfig) {
    if (!(key in schema)) {
      console.warn(`Unknown option: ${key}`);
    }
  }
  
  return userConfig;
}

const config = validateBabelConfig({
  presets: ['es2015'],
  sourceMaps: true,
  filename: 'input.js'
});

Plugin Configuration

Detailed plugin and preset configuration patterns.

// Plugin configuration formats (multiple valid types):
// - string: Plugin name
// - Function: Plugin function  
// - [string]: Plugin name in array
// - [string, Object]: Plugin with options
// - [Function, Object]: Plugin function with options
// - PluginObject: Direct plugin object

// PluginObject structure:
const pluginObject = {
  visitor: Object,          // VisitorObject with AST visitor methods
  pre: Function,            // Pre-transformation hook (optional)
  post: Function,           // Post-transformation hook (optional)
  manipulateOptions: Function, // Option manipulation hook (optional)
  inherits: Object,         // Plugin inheritance (optional)
  name: String             // Plugin name (optional)
};

// VisitorObject structure:
const visitorObject = {
  // Each nodeType can be:
  // - Function (single visitor function)
  // - { enter: Function, exit: Function } (enter/exit visitors)
  // - Array<Function> (multiple visitors)
  Identifier: Function,
  FunctionDeclaration: {
    enter: Function,
    exit: Function
  },
  CallExpression: [Function, Function]  // Multiple visitors
};

Plugin Configuration Examples:

// String plugin names
{
  plugins: [
    "transform-es2015-arrow-functions",
    "transform-es2015-classes"
  ]
}

// Plugin with options
{
  plugins: [
    ["transform-es2015-modules-commonjs", {
      allowTopLevelThis: true,
      strict: false
    }]
  ]
}

// Function plugin
{
  plugins: [
    function customPlugin(babel) {
      const t = babel.types;
      return {
        visitor: {
          Identifier(path) {
            if (path.node.name === "oldName") {
              path.node.name = "newName";
            }
          }
        }
      };
    }
  ]
}

// Plugin object
{
  plugins: [
    {
      visitor: {
        CallExpression: {
          enter(path) {
            console.log("Entering call expression");
          },
          exit(path) {
            console.log("Exiting call expression");
          }
        }
      },
      pre(file) {
        console.log("Starting transformation of:", file.opts.filename);
      },
      post(file) {
        console.log("Finished transformation of:", file.opts.filename);
      }
    }
  ]
}

Preset Configuration

Preset system for bundling multiple plugins with shared configuration.

// Preset configuration formats
type PresetConfig =
  | string                          // Preset name
  | Function                        // Preset function  
  | [string]                        // Preset name in array
  | [string, Object]                // Preset with options
  | [Function, Object]              // Preset function with options
  | PresetObject;                   // Direct preset object

interface PresetObject {
  plugins?: Array<PluginConfig>;    // Plugins in preset
  presets?: Array<PresetConfig>;    // Sub-presets
  options?: Object;                 // Preset options
}

Preset Configuration Examples:

// Basic preset usage
{
  presets: ["es2015", "react"]
}

// Preset with options
{
  presets: [
    ["es2015", {
      modules: false,    // Keep ES6 modules
      loose: true       // Use loose transformations
    }],
    ["react", {
      pragma: "h"       // Use 'h' instead of React.createElement
    }]
  ]
}

// Custom preset function
{
  presets: [
    function customPreset(context, options = {}) {
      return {
        plugins: [
          "transform-es2015-arrow-functions",
          "transform-es2015-classes",
          options.includeRuntime ? "transform-runtime" : null
        ].filter(Boolean)
      };
    }
  ]
}

// Environment-specific presets
{
  presets: [
    ["es2015", { modules: false }]
  ],
  env: {
    test: {
      presets: [
        ["es2015", { modules: "commonjs" }]  // CommonJS for testing
      ]
    }
  }
}

Advanced Plugin Development

Custom Plugin Creation

// Complete plugin with all hooks
function myCustomPlugin(babel) {
  const t = babel.types;
  
  return {
    name: "my-custom-plugin",
    
    // Pre-transformation setup
    pre(file) {
      this.customData = new Map();
      console.log("Processing file:", file.opts.filename);
    },
    
    // Visitor pattern for AST transformation
    visitor: {
      // Transform arrow functions
      ArrowFunctionExpression(path) {
        const { node } = path;
        
        // Convert to function expression
        const funcExpr = t.functionExpression(
          null, // no name
          node.params,
          t.isExpression(node.body) 
            ? t.blockStatement([t.returnStatement(node.body)])
            : node.body
        );
        
        path.replaceWith(funcExpr);
      },
      
      // Track variable declarations
      VariableDeclarator: {
        enter(path) {
          const name = path.node.id.name;
          this.customData.set(name, path.node.init);
        }
      },
      
      // Transform specific identifiers
      Identifier(path) {
        if (path.node.name === "DEBUG" && path.isReferencedIdentifier()) {
          path.replaceWith(t.booleanLiteral(process.env.NODE_ENV !== 'production'));
        }
      }
    },
    
    // Post-transformation cleanup
    post(file) {
      console.log("Transformed variables:", Array.from(this.customData.keys()));
      this.customData.clear();
    },
    
    // Manipulate parser options
    manipulateOptions(opts, parserOpts) {
      // Enable JSX parsing if not already enabled
      if (!parserOpts.plugins.includes('jsx')) {
        parserOpts.plugins.push('jsx');
      }
    }
  };
}

// Use custom plugin
const result = babel.transform(code, {
  plugins: [myCustomPlugin]
});

Plugin State and Options

// Plugin with options and state
function configurablePlugin(babel, options = {}) {
  const t = babel.types;
  const { 
    prefix = 'transformed_',
    exclude = [],
    verbose = false 
  } = options;
  
  return {
    visitor: {
      Identifier: {
        enter(path, state) {
          // Access plugin options
          const shouldTransform = !exclude.includes(path.node.name);
          
          // Access file-level state
          if (!state.transformedCount) {
            state.transformedCount = 0;
          }
          
          if (shouldTransform && path.isReferencedIdentifier()) {
            const newName = prefix + path.node.name;
            path.node.name = newName;
            state.transformedCount++;
            
            if (verbose) {
              console.log(`Transformed identifier: ${path.node.name} -> ${newName}`);
            }
          }
        }
      }
    },
    
    post(file) {
      if (verbose) {
        console.log(`Total transformations: ${file.transformedCount || 0}`);
      }
    }
  };
}

// Usage with options
const result = babel.transform(code, {
  plugins: [
    [configurablePlugin, {
      prefix: 'renamed_',
      exclude: ['React', 'document'],
      verbose: true
    }]
  ]
});

Environment and Conditional Configuration

Environment-Specific Configuration

// EnvironmentConfig structure:
const environmentConfig = {
  env: {
    // Environment-specific configurations
    development: {
      plugins: Array,     // Development plugins
      presets: Array,     // Development presets
      sourceMaps: Boolean // Any other options
    },
    production: {
      plugins: Array,     // Production plugins  
      presets: Array,     // Production presets
      minified: Boolean   // Any other options
    },
    test: {
      plugins: Array,     // Test plugins
      presets: Array      // Test presets
    }
    // Additional environments...
  }
};

Usage Examples:

// Environment-based configuration
const babelConfig = {
  presets: [
    ["es2015", { modules: false }]
  ],
  plugins: [
    "transform-object-rest-spread"
  ],
  env: {
    // Development environment
    development: {
      plugins: [
        "transform-react-jsx-source",     // Add source info for debugging
        "transform-react-jsx-self"        // Add __self prop
      ]
    },
    
    // Production environment  
    production: {
      plugins: [
        "transform-react-remove-prop-types", // Remove PropTypes
        "babel-plugin-transform-react-constant-elements" // Optimize React
      ]
    },
    
    // Test environment
    test: {
      presets: [
        ["es2015", { modules: "commonjs" }] // CommonJS for Jest
      ],
      plugins: [
        "transform-dynamic-import"          // Transform dynamic imports
      ]
    }
  }
};

// Environment is determined by BABEL_ENV or NODE_ENV
process.env.NODE_ENV = 'production';
const result = babel.transform(code, babelConfig);

Conditional Plugin Loading

// Dynamic plugin configuration
function createBabelConfig(options = {}) {
  const plugins = [
    "transform-object-rest-spread"
  ];
  
  // Conditionally add plugins
  if (options.includeRuntime) {
    plugins.push(["transform-runtime", {
      polyfill: false,
      regenerator: true
    }]);
  }
  
  if (options.optimizeReact) {
    plugins.push(
      "transform-react-constant-elements",
      "transform-react-inline-elements"
    );
  }
  
  if (options.removeConsole) {
    plugins.push(["transform-remove-console", {
      exclude: ["error", "warn"]
    }]);
  }
  
  return {
    presets: [
      ["es2015", { modules: options.modules || false }]
    ],
    plugins
  };
}

// Usage
const devConfig = createBabelConfig({
  includeRuntime: true,
  modules: "commonjs"
});

const prodConfig = createBabelConfig({
  optimizeReact: true,
  removeConsole: true
});

Plugin Resolution and Loading

Plugin Resolution Rules

// Plugin resolution follows these patterns:
const resolutionExamples = {
  // Scoped plugins
  "@babel/plugin-transform-arrow-functions": "@babel/plugin-transform-arrow-functions",
  
  // Short names resolve to babel-plugin-*
  "transform-arrow-functions": "babel-plugin-transform-arrow-functions",
  
  // Relative paths
  "./custom-plugin": "./custom-plugin.js",
  
  // Absolute paths  
  "/path/to/plugin": "/path/to/plugin.js",
  
  // Node modules
  "my-babel-plugin": "my-babel-plugin"
};

// Preset resolution follows similar patterns with babel-preset-* prefix

Error Handling

// Plugin loading error handling
try {
  const result = babel.transform(code, {
    plugins: ["nonexistent-plugin"]
  });
} catch (err) {
  if (err.message.includes('Cannot resolve plugin')) {
    console.error('Plugin not found:', err.message);
    // Fallback configuration
    const fallbackResult = babel.transform(code, {
      presets: ["es2015"] // Use preset instead
    });
  }
}

// Preset loading error handling
try {
  const result = babel.transform(code, {
    presets: ["invalid-preset"]  
  });
} catch (err) {
  if (err.message.includes('Cannot resolve preset')) {
    console.error('Preset not found:', err.message);
  }
}