Skip to content

Extraction Techniques

Justin Bush edited this page May 21, 2024 · 7 revisions

Introduction

This section provides a detailed explanation of how SwiftGen uses ts-morph to extract function declarations from TypeScript files. These extracted functions are then converted and included in the generated Swift code.

Setting Up ts-morph

npm install ts-morph

Import ts-morph into your TypeScript project

import { Project } from "ts-morph";

Initializing the Project

Initialize a new project with ts-morph and add the source files that you want to process.

function initializeProject(filePath: string) {
  const project = new Project();
  return project.addSourceFileAtPath(filePath);
}

Extracting Variables

The core function for extracting variable declarations is extractVariables. This function retrieves all variable statements from the source file and maps each declaration to its name.

Code Implementation

Here is the complete implementation of the extractVariables function:

function extractVariables(sourceFile: any) {
  return sourceFile.getVariableStatements()
    .flatMap((statement: any) => statement.getDeclarations().map((decl: any) => decl.getName()));
}

Explanation

  • getVariableStatements: This method retrieves all variable statement nodes in the source file.
  • flatMap: This method flattens the array of variable statements into an array of individual declarations.
  • getDeclarations: This method retrieves all declarations within a variable statement.
  • getName: This method retrieves the name of each variable declaration.

Usage Example

To use the extractVariables function, follow these steps:

  1. Initialize the project with the source file.
  2. Call extractVariables with the initialized source file.
const sourceFile = initializeProject("path/to/your/file.ts");
const variables = extractVariables(sourceFile);
console.log(variables);

This will log an array of variable names found in the specified TypeScript file.

Integrating with SwiftGen

In SwiftGen, after extracting variables, these are combined with other extracted elements (functions, enums, type aliases) to generate the Swift code.

let combinedVariables: string[] = [];

inputFiles.forEach((filePath: string) => {
  try {
    const sourceFile = initializeProject(filePath);
    combinedVariables = combinedVariables.concat(extractVariables(sourceFile));
  } catch (error) {
    console.error(`Error processing file ${filePath}:`, error);
  }
});

Extracting Functions

The main function for extracting function declarations is extractFunctions. This function retrieves all function nodes in the source file and maps them to an object containing the function name, parameters, and type parameters.

Code Implementation

Here is the complete implementation of the extractFunctions function:

function extractFunctions(sourceFile: any) {
  return sourceFile.getFunctions().map((func: any) => ({
    name: func.getName(),
    parameters: func.getParameters().map((param: any) => ({
      name: param.getName(),
      type: convertType(param.getType().getText()),
      default: param.hasInitializer() ? param.getInitializer()?.getText() : undefined
    })),
    typeParameters: func.getTypeParameters().map((param: any) => param.getName())
  }));
}

Explanation

  • getFunctions: This method retrieves all function nodes in the source file.
  • map: This method maps each function node to an object containing its details.
  • getName: Retrieves the name of the function.
  • getParameters: Retrieves all parameters of the function.
    • getName: Retrieves the name of the parameter.
    • getType: Retrieves the type of the parameter.
    • hasInitializer: Checks if the parameter has a default value.
    • getInitializer: Retrieves the default value of the parameter, if any.
  • getTypeParameters: Retrieves all type parameters of the function.

Usage Example

To use the extractFunctions function, follow these steps:

  1. Initialize the project with the source file.
  2. Call extractFunctions with the initialized source file.
const sourceFile = initializeProject("path/to/your/file.ts");
const functions = extractFunctions(sourceFile);
console.log(functions);

This will log an array of objects, each representing a function found in the specified TypeScript file.

Integrating with SwiftGen

In SwiftGen, after extracting functions, these are combined with other extracted elements (variables, enums, type aliases) to generate the Swift code.

let combinedFunctions: any[] = [];

inputFiles.forEach((filePath: string) => {
  try {
    const sourceFile = initializeProject(filePath);
    combinedFunctions = combinedFunctions.concat(extractFunctions(sourceFile));
  } catch (error) {
    console.error(`Error processing file ${filePath}:`, error);
  }
});

Extracting Enums

The extractEnums function retrieves all enum nodes in the source file and maps them to an object containing the enum name and its members.

Code Implementation

Here is the complete implementation of the extractEnums function:

function extractEnums(sourceFile: any) {
  return sourceFile.getEnums().map((enumDecl: any) => ({
    name: enumDecl.getName(),
    members: enumDecl.getMembers().map((member: any) => member.getName())
  }));
}

Explanation

  • getEnums: Retrieves all enum nodes in the source file.
  • map: Maps each enum node to an object containing its details.
  • getName: Retrieves the name of the enum.
  • getMembers: Retrieves all members of the enum.
    • getName: Retrieves the name of each enum member.

Extracting Type Aliases

The extractTypeAliases function retrieves all type alias nodes in the source file and maps them to an object containing the type alias name and its properties.

Code Implementation

Here is the complete implementation of the extractTypeAliases function:

function extractTypeAliases(sourceFile: any) {
  return sourceFile.getTypeAliases().map((alias: any) => ({
    name: alias.getName(),
    properties: alias.getTypeNode().getType().getProperties().map((prop: any) => ({
      name: prop.getName(),
      type: convertType(prop.getTypeAtLocation(prop.getDeclarations()[0]).getText())
    }))
  }));
}

Explanation

  • getTypeAliases: Retrieves all type alias nodes in the source file.
  • map: Maps each type alias node to an object containing its details.
  • getName: Retrieves the name of the type alias.
  • getTypeNode: Retrieves the type node of the alias.
  • getType: Retrieves the type of the node.
  • getProperties: Retrieves all properties of the type.
    • getName: Retrieves the name of each property.
    • getTypeAtLocation: Retrieves the type of the property at the specified location.
    • getText: Retrieves the text representation of the type.

Usage Example

To use the extractEnums and extractTypeAliases functions, follow these steps:

  1. Initialize the project with the source file.
  2. Call extractEnums and extractTypeAliases with the initialized source file.
const sourceFile = initializeProject("path/to/your/file.ts");
const enums = extractEnums(sourceFile);
const typeAliases = extractTypeAliases(sourceFile);

console.log(enums);
console.log(typeAliases);

This will log arrays of objects, each representing an enum or type alias found in the specified TypeScript file.

Integrating with SwiftGen

In SwiftGen, after extracting enums and type aliases, these are combined with other extracted elements (variables, functions) to generate the Swift code.

let combinedEnums: any[] = [];
let combinedTypeAliases: any[] = [];

inputFiles.forEach((filePath: string) => {
  try {
    const sourceFile = initializeProject(filePath);
    combinedEnums = combinedEnums.concat(extractEnums(sourceFile));
    combinedTypeAliases = combinedTypeAliases.concat(extractTypeAliases(sourceFile));
  } catch (error) {
    console.error(`Error processing file ${filePath}:`, error);
  }
});