or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

decorator-support.mdfeature-management.mdfield-transformation.mdindex.mdplugin-creation.mdutilities.md
tile.json

field-transformation.mddocs/

Field Transformation

Comprehensive private and public field transformation including private name mapping, field initialization, and usage transformation.

Capabilities

Build Private Names Map

Creates a mapping of private names to their metadata for transformation.

/**
 * Builds mapping of private names to metadata
 * @param className - Name of the class being transformed
 * @param privateFieldsAsSymbolsOrProperties - Whether to use symbols/properties instead of WeakMap
 * @param props - Array of class property paths
 * @param file - Babel file instance
 * @returns Map of private names to their metadata
 */
function buildPrivateNamesMap(
  className: string,
  privateFieldsAsSymbolsOrProperties: boolean,
  props: PropPath[],
  file: File
): PrivateNamesMap;

type PrivateNamesMap = Map<string, PrivateNameMetadata>;

interface PrivateNameMetadata {
  id: t.Identifier;
  static: boolean;
  method: boolean;
  getId?: t.Identifier;
  setId?: t.Identifier;
  methodId?: t.Identifier;
  initAdded?: boolean;
  getterDeclared?: boolean;
  setterDeclared?: boolean;
}

Usage Examples:

import { buildPrivateNamesMap } from "@babel/helper-create-class-features-plugin";

// Build private names map for a class
const privateNamesMap = buildPrivateNamesMap(
  "MyClass",
  loose, // Use symbols instead of WeakMap
  props, // Class property paths
  file
);

// Access metadata for a private field
const fieldMetadata = privateNamesMap.get("privateField");
if (fieldMetadata) {
  console.log(fieldMetadata.id.name); // Generated identifier
  console.log(fieldMetadata.static);  // Is static field
  console.log(fieldMetadata.method);  // Is method
}

Build Private Names Nodes

Creates AST nodes for private name declarations.

/**
 * Creates AST nodes for private name declarations
 * @param privateNamesMap - Map of private names to metadata
 * @param privateFieldsAsProperties - Use properties instead of WeakMap
 * @param privateFieldsAsSymbols - Use symbols instead of WeakMap
 * @param file - Babel file instance
 * @returns Array of AST statement nodes
 */
function buildPrivateNamesNodes(
  privateNamesMap: PrivateNamesMap,
  privateFieldsAsProperties: boolean,
  privateFieldsAsSymbols: boolean,
  file: File
): t.Statement[];

Build Fields Init Nodes

Builds comprehensive field initialization nodes for classes.

/**
 * Builds field initialization nodes for classes
 * @param ref - Class reference identifier (null for class expressions)
 * @param superRef - Super class expression (undefined if no inheritance)
 * @param props - Array of class property paths
 * @param privateNamesMap - Map of private names to metadata
 * @param file - Babel file instance
 * @param setPublicClassFields - Whether to set public fields as assignments
 * @param privateFieldsAsSymbolsOrProperties - Use symbols/properties for private fields
 * @param noUninitializedPrivateFieldAccess - Skip uninitialized access checks
 * @param constantSuper - Assume super class is constant
 * @param innerBindingRef - Inner class binding identifier
 * @returns Object containing initialization nodes and utilities
 */
function buildFieldsInitNodes(
  ref: t.Identifier | null,
  superRef: t.Expression | undefined,
  props: PropPath[],
  privateNamesMap: PrivateNamesMap,
  file: File,
  setPublicClassFields: boolean,
  privateFieldsAsSymbolsOrProperties: boolean,
  noUninitializedPrivateFieldAccess: boolean,
  constantSuper: boolean,
  innerBindingRef: t.Identifier | null
): FieldsInitResult;

interface FieldsInitResult {
  staticNodes: t.Statement[];
  pureStaticNodes: t.FunctionDeclaration[];
  instanceNodes: t.ExpressionStatement[];
  lastInstanceNodeReturnsThis: boolean;
  classBindingNode: t.Statement | null;
  wrapClass: (path: NodePath<t.Class>) => NodePath;
}

Usage Examples:

import { buildFieldsInitNodes } from "@babel/helper-create-class-features-plugin";

// Build field initialization for a class
const {
  staticNodes,
  pureStaticNodes,
  instanceNodes,
  lastInstanceNodeReturnsThis,
  classBindingNode,
  wrapClass
} = buildFieldsInitNodes(
  ref,
  path.node.superClass,
  props,
  privateNamesMap,
  file,
  setPublicClassFields ?? loose,
  privateFieldsAsSymbolsOrProperties ?? loose,
  noUninitializedPrivateFieldAccess,
  constantSuper ?? loose,
  innerBinding
);

// Apply transformations
const wrappedPath = wrapClass(path);
wrappedPath.insertBefore(staticNodes);
wrappedPath.insertAfter(pureStaticNodes);

Transform Private Names Usage

Transforms all private name usage throughout the class.

/**
 * Transforms private name usage throughout the class
 * @param classRefForDefine - Class reference for private field definitions
 * @param path - Class path to transform
 * @param privateNamesMap - Map of private names to metadata
 * @param options - Transformation options
 * @param file - Babel file instance
 */
function transformPrivateNamesUsage(
  classRefForDefine: t.Identifier,
  path: NodePath<t.Class>,
  privateNamesMap: PrivateNamesMap,
  options: {
    privateFieldsAsProperties: boolean;
    noUninitializedPrivateFieldAccess: boolean;
    noDocumentAll: boolean;
    innerBinding: t.Identifier | null;
  },
  file: File
): void;

Build Check In RHS

Builds checks for right-hand side private property access (#prop in obj).

/**
 * Builds private property access checks for 'in' expressions
 * @param privateNamesMap - Map of private names to metadata
 * @param loose - Whether to use loose mode checks
 * @param privateFieldsAsSymbols - Whether private fields use symbols
 * @returns Expression for checking private property access
 */
function buildCheckInRHS(
  privateNamesMap: PrivateNamesMap,
  loose: boolean,
  privateFieldsAsSymbols: boolean
): t.Expression;

Usage Examples:

import { buildCheckInRHS } from "@babel/helper-create-class-features-plugin";

// Build check for private property 'in' expressions
const checkExpression = buildCheckInRHS(
  privateNamesMap,
  loose,
  privateFieldsAsSymbols
);

// Used to transform: #privateField in obj
// Into appropriate runtime check

Private Name Visitor Factory

Creates visitors for traversing and transforming private name usage.

/**
 * Factory for creating private name visitors
 * @param visitor - Base visitor configuration
 * @returns Configured visitor for private name transformation
 */
function privateNameVisitorFactory<S, V>(
  visitor: PrivateNameVisitor<S, V>
): PrivateNameVisitor<S, V>;

interface PrivateNameVisitorState<V> {
  privateNamesMap: PrivateNamesMapGeneric<V>;
  redeclared?: string[];
}

interface PrivateNameVisitor<S, V> {
  ClassExpression?(path: NodePath<t.ClassExpression>, state: S): void;
  ClassDeclaration?(path: NodePath<t.ClassDeclaration>, state: S): void;
  PrivateName?(path: NodePath<t.PrivateName>, state: S): void;
}

Property Path Types

Defines the types of class properties that can be transformed.

/**
 * Union type for all class property node types
 */
type PropNode = 
  | t.ClassProperty 
  | t.ClassPrivateProperty 
  | t.ClassPrivateMethod 
  | t.StaticBlock;

/**
 * NodePath for class property nodes
 */
type PropPath = NodePath<PropNode>;

Field Initialization Patterns

The system supports various field initialization patterns:

// Instance fields
class Example {
  publicField = "value";     // Becomes assignment in constructor
  #privateField = "value";   // Uses WeakMap or symbol storage
}

// Static fields  
class Example {
  static publicStatic = "value";    // Becomes class.prop = value
  static #privateStatic = "value";  // Uses WeakMap or symbol storage
}

// Computed fields
class Example {
  [computedKey] = "value";          // Key extracted and memoized
  #[computedPrivate] = "value";     // Not supported - syntax error
}

Loose Mode vs Strict Mode

The transformation behavior changes based on loose mode:

Strict Mode (default):

  • Private fields use WeakMap for storage
  • Full spec compliance with runtime checks
  • Proper handling of uninitialized field access

Loose Mode:

  • Private fields use symbols or direct properties
  • Faster execution, less spec compliant
  • Minimal runtime checks