Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Expose Typescript -> ESTree AST conversion logic #350

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

Closed
deifactor opened this issue Mar 12, 2019 · 16 comments
Closed

Expose Typescript -> ESTree AST conversion logic #350

deifactor opened this issue Mar 12, 2019 · 16 comments
Labels
package: typescript-estree Issues related to @typescript-eslint/typescript-estree question Questions! (i.e. not a bug / enhancment / documentation)

Comments

@deifactor
Copy link

I'm working on project that wants to parse some code as TypeScript, get the corresponding ESTree AST, and then do some logic that requires both the TypeScript API objects and the converted ESTree output. Right now I'm directly importing @typescript-eslint/typescript-estree/dist/convert.js, which makes me feel gross inside. Would it be reasonable to either expose that module in the typescript-estree package or split it off into its own package?

@deifactor deifactor added package: typescript-estree Issues related to @typescript-eslint/typescript-estree triage Waiting for team members to take a look labels Mar 12, 2019
@mohsen1
Copy link
Contributor

mohsen1 commented Mar 12, 2019

related to #330

@bradzacher
Copy link
Member

I might be misunderstanding, but can't you use parseAndGenerateServices exported from @typescript-eslint/typescript-estree?
That will return { ast, services } where ast is the ESTree AST, and services is ParserServices.

@bradzacher bradzacher added question Questions! (i.e. not a bug / enhancment / documentation) and removed triage Waiting for team members to take a look labels Mar 13, 2019
@deifactor
Copy link
Author

I tried doing that, but for some reason it's significantly slower than doing it myself when I'm running over many files (maybe because it's not properly reusing the typechecking machinery, not sure).

@bradzacher
Copy link
Member

I don't know your code, but I would say that your code is faster because you're not setting up the typescript Program.

You cannot generate the TS AST -> ESTree AST map without having typescript generate the Program. This is slow because in order for typescript to generate the AST, it has to parse the entire project (because it needs all of the type information to generate the correct information). It's a cost you should only have to pay once per tsconifg.json supplied.

@deifactor
Copy link
Author

I am setting up the Program in my code. It's entirely possible that I was just calling typescript-estree wrong in a way that wasn't allowing it to reuse the tsconfig.json properly.

@bradzacher
Copy link
Member

See #243 for an analysis of the performance of typescript-estree. The program generation performance hit should be proportional to the size of your codebase, not the number of files being parsed. We're still looking for a solution to this.
If used correctly, it should only construct the program once.

Correct usage of parseAndGenerateServices: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/parser/src/parser.ts#L44-L104

@deifactor
Copy link
Author

deifactor commented Mar 13, 2019

Here's how I'm calling parseAndGenerateServices:

for (const file of files) {
  const { ast, services } = tsest.parseAndGenerateServices(fs.readFileSync(file, "utf8"), {
      project: tsconfigJsonPath,
      extraFileExtensions: ["js"],
      tsconfigRootDir: inputDir
  });
  // more stuff
}

Note that the tsconfig.json is outside of the project's working directory, since I'm using this to analyze Javascript, so I'm generating a synthetic tsconfig.json file. But the project file and tsconfigRootDir are the same for each file.

My faster code (which I don't know if I can post here) does create a program, and is able to successfully invoke Converter.convertProgram() and Convert.getASTMaps() (and I can verify that the resulting maps and ASTs are sensible).

@bradzacher
Copy link
Member

bradzacher commented Mar 13, 2019

// should be absolute if possible
const project = path.resolve(__dirname, "../../somepath/to/tsconfig.json");
const tsconfigRootDir = path.dirname(project);

for (const file of files) {
  const { ast, services } = tsest.parseAndGenerateServices(fs.readFileSync(file, "utf8"), {
    extraFileExtensions: ["js"],
	project,
	tsconfigRootDir,
    // should also pass in the file path
	filePath: file,
  });

  // more stuff
}

@deifactor
Copy link
Author

Aha, passing in the file path seems to make it fast as what I was doing manually.

@deifactor
Copy link
Author

I no longer need the original thing I was asking for; thank you for the help! Feel free to close it if you want.

@deifactor
Copy link
Author

deifactor commented Jun 12, 2019

Re-commenting on this because I wound up wanting it again, for... some very silly reasons. I'm doing horrible things to Typescript that go beyond the original use case of 'use it as a linter' (I'm modifying the host to apply some AST transformations before parsing/typechecking), and in general the only things I actually need are convert.ts and the TSESTree type definition. I don't think I can mess around with the created host using the provided API, and I think even if I did, it's likely I'd run into some other thing that I'd need.

@bradzacher
Copy link
Member

I was under the impression that typescript will give you the AST after it's done its typecheck cycle?

@deifactor
Copy link
Author

deifactor commented Jun 12, 2019

You can shim the compiler host's getSourceFile to call the original host's getSourceFile, then apply whatever transformation logic you like. This happens before typechecking. I've verified that my approach works by importing dist/convert.js directly, but that's gross and I'd rather not import internals :)

But you're right, normally you can't get the AST without typechecking.

@bradzacher
Copy link
Member

It sounds like you're doing some super weird things.
What are you trying to do? It seems like you're almost constructing a file on the fly?

@deifactor
Copy link
Author

Okay, I ran into this issue again.

Here's what I'm currently trying to do (which is somewhat different from what I was trying to do earlier): I have an AST explorer that annotates the AST with type information. It runs in the browser, so I can't use the existing typescript-estree infrastructure since that doesn't let me override the compiler host. So right now I'm constructing a host that just emulates a simple filesystem, then constructing a ts.SourceFile appropriately and passing it through the internal typescript-estree converter. So, I don't actually care about all of the infrastructure typescript-eslint does to generate programs, set up watching, and so on, since I already have my own host and such. The only thing I care about is getting an estree AST that lets me get the corresponding TypeScript nodes so I can get their types, which is exactly the API the Converter exposes.

@bradzacher
Copy link
Member

Feel free to put up a PR if you want to expose it!

@typescript-eslint typescript-eslint locked as resolved and limited conversation to collaborators Feb 21, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
package: typescript-estree Issues related to @typescript-eslint/typescript-estree question Questions! (i.e. not a bug / enhancment / documentation)
Projects
None yet
Development

No branches or pull requests

3 participants