Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pull Types For File #95

Open
jsfour opened this issue Mar 21, 2023 · 31 comments
Open

Pull Types For File #95

jsfour opened this issue Mar 21, 2023 · 31 comments

Comments

@jsfour
Copy link
Member

jsfour commented Mar 21, 2023

We need to create a subclass of DependencyGraphParser here
https://github.com/octaviuslabs/walter/blob/main/src/dependency-graph-parser.ts#L49 where the build function pulls in the types (not the full code blocks) for the target file.

@jsfour
Copy link
Member Author

jsfour commented Mar 21, 2023

@imwalterbot thoughts on how to go about this?

@imwalterbot
Copy link
Contributor

@imwalterbot thoughts on how to go about this?

Queued for processing...

@imwalterbot
Copy link
Contributor

@imwalterbot thoughts on how to go about this?

Processing this now

@imwalterbot
Copy link
Contributor

Based on the provided context, you can create a subclass of DependencyGraphParser that overrides the build function to only pull in the types for the target file. Here's an example of how to create the subclass:

class TypeOnlyDependencyGraphParser extends DependencyGraphParser {
  constructor(gitHubFileUrl: string) {
    super(gitHubFileUrl);
  }

  public async build(): Promise<DepGraph<ts.SourceFile>> {
    Log.info(
      `Building type-only dependency graph for ${this.targetFile.url} excluding target`
    );
    const deps = await this.getDependencies(this.targetFile);

    for (const dep of deps.deps) {
      await this.processFileForTypes(dep);
    }
    return this.graph;
  }

  private async processFileForTypes(url: ParsedGitHubURL): Promise<void> {
    Log.info(`Processing file for types ${url.url}`);
    if (this.graph.hasNode(url.url)) {
      Log.info(`Node exists ${url.url}`);
      return;
    }

    const src = await this.getDependencies(url);
    const typeOnlySourceFile = this.extractTypeDeclarations(src.sourceFile);
    this.graph.addNode(url.url, typeOnlySourceFile);

    for (const dep of src.deps) {
      await this.processFileForTypes(dep);
      Log.info(`Adding edge ${src.url.url} -> ${dep.url}`);
      this.graph.addDependency(src.url.url, dep.url);
    }
  }

  private extractTypeDeclarations(sourceFile: ts.SourceFile): ts.SourceFile {
    // Pseudocode for extracting only type declarations from the source file
    // 1. Iterate through the nodes of the source file
    // 2. If the node is a type declaration, add it to a new source file
    // 3. Return the new source file containing only type declarations

    // Implement the actual logic for extracting type declarations here
  }
}

This subclass, TypeOnlyDependencyGraphParser, overrides the build function to use a new private function processFileForTypes which processes the file and adds only the type declarations to the dependency graph. The extractTypeDeclarations function is a placeholder for the logic to extract type declarations from the source file. You'll need to implement this logic based on your requirements.

@jsfour
Copy link
Member Author

jsfour commented Mar 21, 2023

Could we use the typescript compiler in extractTypeDeclarations ?

@imwalterbot
Copy link
Contributor

Could we use the typescript compiler in extractTypeDeclarations ?

Queued for processing...

@imwalterbot
Copy link
Contributor

Could we use the typescript compiler in extractTypeDeclarations ?

Processing this now

@imwalterbot
Copy link
Contributor

Yes, you can use the TypeScript compiler in the extractTypeDeclarations function to extract type declarations from the source file. Here's an example of how to do this:

private extractTypeDeclarations(sourceFile: ts.SourceFile): ts.SourceFile {
  const typeDeclarations: ts.Node[] = [];

  const visitNode = (node: ts.Node): void => {
    if (ts.isTypeAliasDeclaration(node) || ts.isInterfaceDeclaration(node) || ts.isEnumDeclaration(node)) {
      typeDeclarations.push(node);
    } else {
      ts.forEachChild(node, visitNode);
    }
  };

  ts.forEachChild(sourceFile, visitNode);

  // Create a new source file with only type declarations
  const typeDeclarationSourceFile = ts.updateSourceFileNode(sourceFile, typeDeclarations);

  return typeDeclarationSourceFile;
}

In this example, we define a visitNode function that checks if the current node is a type alias, interface, or enum declaration. If it is, we add it to the typeDeclarations array. We then use ts.forEachChild to recursively visit all nodes in the source file. Finally, we create a new source file with only the type declarations using ts.updateSourceFileNode.

You can then use this extractTypeDeclarations function in the TypeOnlyDependencyGraphParser subclass as described in the previous answer.

@jsfour
Copy link
Member Author

jsfour commented Mar 21, 2023

What if we only waned the types that the gitHubFileUrl fiile depends on? I think you would need to modify getDependencies.

@imwalterbot
Copy link
Contributor

What if we only waned the types that the gitHubFileUrl fiile depends on? I think you would need to modify getDependencies.

Queued for processing...

@imwalterbot
Copy link
Contributor

What if we only waned the types that the gitHubFileUrl fiile depends on? I think you would need to modify getDependencies.

Processing this now

@imwalterbot
Copy link
Contributor

If you want to modify the getDependencies function to only include the types that the gitHubFileUrl file depends on, you can do the following:

  1. Traverse the sourceFile AST to find the import declarations.
  2. For each import declaration, check if it imports a type.
  3. If it imports a type, add it to the deps array.

Here's an example of how to modify the getDependencies function:

private async getDependencies(targetFile: ParsedGitHubURL): Promise<{
  sourceFile: ts.SourceFile;
  deps: ParsedGitHubURL[];
  url: ParsedGitHubURL;
}> {
  Log.info(`Getting dependencies for ${targetFile.url}`);
  const fileContent = await utils.getFileFromUrl(targetFile.url);

  const sourceFile = ts.createSourceFile(
    fileContent.parsedUrl.filePath,
    fileContent.body,
    ts.ScriptTarget.ES2015,
    true
  );

  const deps: ParsedGitHubURL[] = [];
  const branch = this.branch;

  sourceFile.forEachChild((node) => {
    if (ts.isImportDeclaration(node)) {
      const importPath = (node.moduleSpecifier as ts.StringLiteral).text;
      if (!importPath.startsWith(".") && !importPath.startsWith("/")) {
        Log.info(`Skipping import ${importPath}`);
        return;
      }

      // Check if the import declaration imports a type
      const importClause = node.importClause;
      if (importClause && importClause.namedBindings) {
        const namedBindings = importClause.namedBindings;
        if (ts.isNamedImports(namedBindings)) {
          for (const element of namedBindings.elements) {
            if (element.name && ts.isIdentifier(element.name)) {
              // If the imported element has a type keyword, add it to deps
              if (element.typeKeyword) {
                const imported = calculateAbsolutePath(
                  targetFile.filePath,
                  importPath + ".ts"
                );

                Log.info(`Adding import ${imported}`);
                deps.push(branch.urlForPath(imported));
              }
            }
          }
        }
      }
    }
  });

  return {
    sourceFile,
    deps,
    url: targetFile,
  };
}

This modification will ensure that the deps array only contains the types that the gitHubFileUrl file depends on. Note that this assumes the imports are using named imports. If there are other import styles, you might need to handle them accordingly.

@jsfour
Copy link
Member Author

jsfour commented Mar 21, 2023

Ok summarize what we are looking to do.

@imwalterbot
Copy link
Contributor

Ok summarize what we are looking to do.

Queued for processing...

@imwalterbot
Copy link
Contributor

Ok summarize what we are looking to do.

Processing this now

@imwalterbot
Copy link
Contributor

Based on the provided DependencyGraphParser class, you want to create a subclass that only includes the types that the gitHubFileUrl file depends on. To achieve this, you need to modify the getDependencies function to only include type dependencies, and then use this modified function in the subclass.

Here's a summary of the steps:

  1. Create a subclass of DependencyGraphParser called TypeDependencyGraphParser.
  2. In the subclass, override the build function to use a new private function processFileForTypes which processes the file and adds only the type dependencies to the dependency graph.
  3. Modify the getDependencies function to only include the types that the gitHubFileUrl file depends on. This can be done by checking if the import declarations in the source file import a type, and if they do, add it to the deps array.

Here's an example of the TypeDependencyGraphParser subclass:

class TypeDependencyGraphParser extends DependencyGraphParser {
  constructor(gitHubFileUrl: string) {
    super(gitHubFileUrl);
  }

  public async build(): Promise<DepGraph<ts.SourceFile>> {
    Log.info(
      `Building type dependency graph for ${this.targetFile.url} excluding target`
    );
    const deps = await this.getDependencies(this.targetFile);

    for (const dep of deps.deps) {
      await this.processFileForTypes(dep);
    }
    return this.graph;
  }

  private async processFileForTypes(url: ParsedGitHubURL): Promise<void> {
    Log.info(`Processing file for types ${url.url}`);
    if (this.graph.hasNode(url.url)) {
      Log.info(`Node exists ${url.url}`);
      return;
    }

    const src = await this.getDependencies(url);
    this.graph.addNode(url.url, src.sourceFile);

    for (const dep of src.deps) {
      await this.processFileForTypes(dep);
      Log.info(`Adding edge ${src.url.url} -> ${dep.url}`);
      this.graph.addDependency(src.url.url, dep.url);
    }
  }

  // Modify the getDependencies function to only include type dependencies
  private async getDependencies(targetFile: ParsedGitHubURL): Promise<{
    sourceFile: ts.SourceFile;
    deps: ParsedGitHubURL[];
    url: ParsedGitHubURL;
  }> {
    // (Include the modified getDependencies function from the previous answer here)
  }
}

This subclass, TypeDependencyGraphParser, overrides the build function to use the new private function processFileForTypes which processes the file and adds only the type dependencies to the dependency graph. The modified getDependencies function ensures that the deps array only contains the types that the gitHubFileUrl file depends on.

@jsfour
Copy link
Member Author

jsfour commented Mar 21, 2023

Seems like you can use the existing getDependencies right?

@imwalterbot
Copy link
Contributor

Seems like you can use the existing getDependencies right?

Queued for processing...

@imwalterbot
Copy link
Contributor

Seems like you can use the existing getDependencies right?

Processing this now

@imwalterbot
Copy link
Contributor

Yes, you're right. You can use the existing getDependencies function and create a new subclass TypeDependencyGraphParser that extends DependencyGraphParser. Override the build function in the subclass to process files for types and build a dependency graph containing only the type dependencies.

Here's an example of the TypeDependencyGraphParser subclass:

class TypeDependencyGraphParser extends DependencyGraphParser {
  constructor(gitHubFileUrl: string) {
    super(gitHubFileUrl);
  }

  public async build(): Promise<DepGraph<ts.SourceFile>> {
    Log.info(
      `Building type dependency graph for ${this.targetFile.url} excluding target`
    );
    const deps = await this.getDependencies(this.targetFile);

    for (const dep of deps.deps) {
      await this.processFileForTypes(dep);
    }
    return this.graph;
  }

  private async processFileForTypes(url: ParsedGitHubURL): Promise<void> {
    Log.info(`Processing file for types ${url.url}`);
    if (this.graph.hasNode(url.url)) {
      Log.info(`Node exists ${url.url}`);
      return;
    }

    const src = await this.getDependencies(url);
    const typeOnlySourceFile = this.extractTypeDeclarations(src.sourceFile);
    this.graph.addNode(url.url, typeOnlySourceFile);

    for (const dep of src.deps) {
      await this.processFileForTypes(dep);
      Log.info(`Adding edge ${src.url.url} -> ${dep.url}`);
      this.graph.addDependency(src.url.url, dep.url);
    }
  }

  private extractTypeDeclarations(sourceFile: ts.SourceFile): ts.SourceFile {
    // (Include the extractTypeDeclarations function from the previous answer here)
  }
}

This subclass, TypeDependencyGraphParser, overrides the build function to use the new private function processFileForTypes which processes the file and adds only the type dependencies to the dependency graph. The extractTypeDeclarations function will extract type declarations from the source file, which can be added to the graph.

@jsfour
Copy link
Member Author

jsfour commented Mar 21, 2023

@imwalterbot
Copy link
Contributor

@imwalterbot APPROVED for https://github.com/octaviuslabs/walter/blob/main/src/dependency-graph-parser.ts changes

Queued for processing...

@imwalterbot
Copy link
Contributor

@jsfour
Copy link
Member Author

jsfour commented Mar 21, 2023

Will extractTypeDeclarations also include function declarations? I'm trying to build a type hint system and it would be good to have those as well.

@imwalterbot
Copy link
Contributor

Will extractTypeDeclarations also include function declarations? I'm trying to build a type hint system and it would be good to have those as well.

Queued for processing...

@imwalterbot
Copy link
Contributor

Will extractTypeDeclarations also include function declarations? I'm trying to build a type hint system and it would be good to have those as well.

Processing this now

@imwalterbot
Copy link
Contributor

To include function declarations in the extractTypeDeclarations function, you can modify the function to also check for function declarations while traversing the sourceFile AST. Here's an updated version of the extractTypeDeclarations function that includes function declarations:

private extractTypeDeclarations(sourceFile: ts.SourceFile): ts.SourceFile {
  const typeDeclarations: ts.Node[] = [];

  const visitNode = (node: ts.Node): void => {
    if (
      ts.isTypeAliasDeclaration(node) ||
      ts.isInterfaceDeclaration(node) ||
      ts.isEnumDeclaration(node) ||
      ts.isFunctionDeclaration(node)
    ) {
      typeDeclarations.push(node);
    } else {
      ts.forEachChild(node, visitNode);
    }
  };

  ts.forEachChild(sourceFile, visitNode);

  // Create a new source file with only type declarations and function declarations
  const typeDeclarationSourceFile = ts.updateSourceFileNode(sourceFile, typeDeclarations);

  return typeDeclarationSourceFile;
}

In this updated version, we've added ts.isFunctionDeclaration(node) to the condition in the visitNode function. This will include function declarations along with type declarations when building the type hint system.

@octaviuslabs octaviuslabs deleted a comment from imwalterbot Mar 22, 2023
@octaviuslabs octaviuslabs deleted a comment from imwalterbot Mar 22, 2023
@jsfour
Copy link
Member Author

jsfour commented Mar 22, 2023

@imwalterbot given the above how would we output the only the definitions (not the full function) in the toString method on the class?

@imwalterbot
Copy link
Contributor

@imwalterbot given the above how would we output the only the definitions (not the full function) in the toString method on the class?

Queued for processing...

@imwalterbot
Copy link
Contributor

@imwalterbot given the above how would we output the only the definitions (not the full function) in the toString method on the class?

Processing this now

@imwalterbot
Copy link
Contributor

To output only the definitions (and not the full function) in the toString method, you can modify the toStrings method to print only the type and function signatures instead of the entire source file. You can achieve this by using a custom printing function that extracts the type and function signatures from the source file.

Here's an example of a custom printing function called printTypeAndFunctionSignatures:

private printTypeAndFunctionSignatures(sourceFile: ts.SourceFile): string {
  const printer = ts.createPrinter();
  let signatures: string[] = [];

  const visitNode = (node: ts.Node): void => {
    if (
      ts.isTypeAliasDeclaration(node) ||
      ts.isInterfaceDeclaration(node) ||
      ts.isEnumDeclaration(node) ||
      ts.isFunctionDeclaration(node)
    ) {
      signatures.push(printer.printNode(ts.EmitHint.Unspecified, node, sourceFile));
    } else {
      ts.forEachChild(node, visitNode);
    }
  };

  ts.forEachChild(sourceFile, visitNode);

  return signatures.join("\n");
}

Now, modify the toStrings method to use this custom printing function:

public toStrings(): string[] {
  const order = this.graph.overallOrder();

  let out: string[] = [];
  for (const node of order) {
    const data = this.graph.getNodeData(node);
    out.push(`// SOURCE_FILE: ${node}`);
    out.push(this.printTypeAndFunctionSignatures(data));
    out.push(`// END: ${node}`);
  }
  return out;
}

With these changes, the toString method will output only the type and function signatures, which can be useful for building a type hint system.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants