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

OpenBraceToken was not found #665

Closed
nchanged opened this issue Jul 17, 2019 · 14 comments
Closed

OpenBraceToken was not found #665

nchanged opened this issue Jul 17, 2019 · 14 comments
Labels

Comments

@nchanged
Copy link

nchanged commented Jul 17, 2019

Hi

There is a problem with this snippet

import { Project } from 'ts-morph';
import * as ts from 'typescript';
function createFile(contents: string) {
  const project = new Project();
  return project.createSourceFile('src/MyClass.ts', contents);
}

const file = createFile(`
function oi(){
  const snapshot = { isDraggingOver: false };
  function getSourceStyle(opts) {
    return {};
  }
  return <div>
        <div></div>
        <p style={getSourceStyle(snapshot.isDraggingOver)}></p>
      </div>
}
`);

file.getDescendantsOfKind(ts.SyntaxKind.Identifier);

Basically, removing <div></div> helps, but otherwise it breaks on this JSX file. And the JSX is valid. Also removing an argument from getSourceStyle() works just fine.

@martaver
Copy link

Example of react component suffering from the same problem:

import * as React from 'react';

const getStyle = (arg: boolean) => ({
    color: `${ arg ? 'red' : 'green'}`
})
export const Broken: React.SFC<{ arg: boolean }> = (props) => (
    <div style={getStyle(props.arg)} >...</div>
)

@dsherret
Copy link
Owner

Does it work when the file has a .tsx extension?

Could you also follow the issue template and post the error message? Thanks, will look into this later.

@martaver
Copy link

In my case, the file has a tsx extension, yes. The error message I get is:

Source transform default/typescript/sim/src/components/DragNDrop/DragNDrop.jsx(node:20537) UnhandledPromiseRejectionWarning: Error: Unexpected scenario where a(n) OpenBraceToken was not found.
    at getTokenEnd (/Users/martaver/Projects/smoothteam/git/smoothteam/node_modules/ts-morph/dist/compiler/ast/utils/CommentNodeParser.js:87:23)
    at Function.getContainerBodyPos (/Users/martaver/Projects/smoothteam/git/smoothteam/node_modules/ts-morph/dist/compiler/ast/utils/CommentNodeParser.js:78:20)
    at isStatementMemberOrPropertyHoldingSyntaxList (/Users/martaver/Projects/smoothteam/git/smoothteam/node_modules/ts-morph/dist/compiler/ast/utils/ExtendedParser.js:60:58)
    at Function.getCompilerChildren (/Users/martaver/Projects/smoothteam/git/smoothteam/node_modules/ts-morph/dist/compiler/ast/utils/ExtendedParser.js:41:13)
    at getCompilerDescendantsIterator (/Users/martaver/Projects/smoothteam/git/smoothteam/node_modules/ts-morph/dist/compiler/ast/common/Node.js:1553:48)
    at getCompilerDescendantsIterator.next (<anonymous>)
    at getCompilerDescendantsIterator (/Users/martaver/Projects/smoothteam/git/smoothteam/node_modules/ts-morph/dist/compiler/ast/common/Node.js:1555:16)
    at getCompilerDescendantsIterator.next (<anonymous>)
    at getCompilerDescendantsIterator (/Users/martaver/Projects/smoothteam/git/smoothteam/node_modules/ts-morph/dist/compiler/ast/common/Node.js:1555:16)
    at getCompilerDescendantsIterator.next (<anonymous>)
(node:20537) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:20537) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

@dsherret
Copy link
Owner

By the way, change this:

import { Project } from 'ts-morph';
import * as ts from 'typescript';

To this to ensure the same ts version is used that ts-morph uses:

import { Project, ts } from 'ts-morph';

@martaver thanks! Will look into it. Looks like single statement arrow functions are not handled properly in the comment parser.

@dsherret dsherret added the bug label Jul 17, 2019
@dsherret
Copy link
Owner

dsherret commented Jul 17, 2019

@nchanged changing 'src/MyClass.ts' to 'src/MyClass.tsx' seems to resolve that scenario. I'll look into the one @martaver mentioned later this week (probably on the weekend... I have a busy week this week).

@martaver
Copy link

Thanks for your fast reply!

@nchanged
Copy link
Author

@dsherret thanks!! that does seem to work, however, I can't seem to figure out how to name those
project.createSourceFile(sourcePath, module.contents);
if I give it an existing path it says that the file exists, if I give it something else it says that it doesn't. A bit confusing. As a matter of fact, I don't really need to specify any file name since I am loading the contents manually. Can the name be optional?

@dsherret
Copy link
Owner

@nchanged

if I give it something else it says that it doesn't

It shouldn't mention anything if the file doesn't exist. Is it really saying it doesn't exist? Please open an issue if so.

By default, the Project will work with the actual file system and so if you create a file at a location that exists then it will throw (that message should direct you to provide an { overwrite: true } option if you want it to overwrite the existing file). If you want a virtual/in memory file system, you can read about that here.

The TypeScript compiler api requires a source file name for every file and will use that information to figure out how to parse the file (ex. if it is a .tsx extension then it will parse it as a tsx file).

dsherret added a commit that referenced this issue Jul 17, 2019
@dsherret
Copy link
Owner

I'm only able to reproduce these scenarios in non-jsx/tsx files. For some reason when doing this, the compiler will parse block nodes with zero length and this causes the issue in ts-morph (since it expects a block to have an open brace token):

image

Probably a bug in the compiler, but this is too obscure to bother reporting.

Anyway, I've fixed this and will do a release later, but recommend ensuring that the source files are created as .tsx or .jsx files so they're properly parsed.

@dsherret
Copy link
Owner

And published in 3.1.3. Please let me know if there are any other issues!

@nchanged
Copy link
Author

nchanged commented Jul 18, 2019

@dsherret thanks for that.

{ useVirtualFileSystem: true }

I added this option, however, it didn't solve anything. Passing an absolute path to a file that exists in the real fs, throws an error Error: Could not find file.

The only thing that works is MyClass.tsx that's pretty frustrating, since I don't want to depend on the filesystem at all and I don't need saving nor reading, I do the analysis in-memory only.

Why wouldn't it throw an error on MyClass.tsx and throws on a file that actually exists (complaining that it doesn't)?

UPD.

const sourcePath = 'MyClass.tsx';
project.createSourceFile(sourcePath, module.contents);

My module can be anything - js, jsx, ts, tsx But I have to use MyClass.tsx on all files (it looks very silly). But I do have abs paths for all of them.

@dsherret
Copy link
Owner

@nchanged yeah, if you use a virtual file system (in memory file system) then it will be in memory and it will not access anything on the actual file system. That doesn't seem like what you want for that scenario.

Why wouldn't it throw an error on MyClass.tsx and throws on a file that actually exists (complaining that it doesn't)?

Could you be more specific with the question? Perhaps open a new issue so it's not so off-topic with this one.

If I'm understanding this correctly, if the file system looks like this:

/MyClass.tsx

...and someone does...

project.createSourceFile("/MyClass.tsx", "class MyClass {}");
project.createSourceFile("/MyClass2.tsx", "class MyClass {}");

... you are wondering why it throws the error "A source file already exists at the provided file path: /MyClass.tsx" only for the first statement? It's because the file already exists at /MyClass.tsx, but not at /MyClass2.tsx. Why would you expect it to throw on the second statement? If someone is creating a file and it doesn't exist then there's no reason to throw an error.

If you don't want this error message for the first statement, then you have to be explicit and provide the overwrite option:

project.createSourceFile("/MyClass.tsx", "class MyClass {}", { overwrite: true });

I've opened #666 to include details about the overwrite option in the error message. I thought it was already doing that, but I must have missed it for this scenario.

My module can be anything - js, jsx, ts, tsx But I have to use MyClass.tsx on all files (it looks very silly). But I do have abs paths for all of them.

You can make the filename anything you like. You don't need to make it MyClass.tsx on all files. The compiler api expects a source file name though:

const sourceFile = ts.createSourceFile(fileName, fileText);

Also, you can't make it MyClass.tsx on all files because then something like this wouldn't parse properly:

// assertion, but parses as JsxElement
const t = <string>myNumber;

That file would need a .ts extension.

The only thing that works is MyClass.tsx that's pretty frustrating, since I don't want to depend on the filesystem at all and I don't need saving nor reading, I do the analysis in-memory only.

There are more scenarios that work than the text MyClass.tsx and you are depending on the file system in order to load the files. You are making changes to the project in memory though and it needs to know the location of the file in order to resolve modules. For example, if your MyClass.tsx looks like this:

import { OtherClass } from "./OtherClass.tsx";

How would it know how to resolve the module specifier "OtherClass.tsx" if the current file didn't have a file path?

Anyway, it's for resolving modules that a source file needs a file path and even if it has no modules it still needs to figure out how to parse the file based on the file extension (caveat: there is also a scriptKind option that can be used to specify how to parse the source file, but the compiler api requires a file name anyway).

@nchanged
Copy link
Author

nchanged commented Jul 18, 2019

@dsherret the problem is that if I give it a real absolute path that exists, it throws this error

Error: Could not find file: '/Users/.../node_modules/react/index.js'.
    at getValidSourceFile (/Users/.../node_modules/typescript/lib/typescript.js:118577:23)
    at Object.findReferences (/Users/.../node_modules/typescript/lib/typescript.js:118928:117)
    at LanguageService.findReferencesAtPosition (/Users/.../node_modules/ts-morph/dist/compiler/tools/LanguageService.js:171:45)
    at LanguageService.findReferences (/Users/.../node_modules/ts-morph/dist/compiler/tools/LanguageService.js:142:21)

That's my main issue ATM. /Users/.../node_modules/react/index.js exists 100%.

How would it know how to resolve the module specifier "OtherClass.tsx" if the current file didn't have a file path?

I handle all references manually (I have my own dependency tree) So It's all resolved.

@dsherret
Copy link
Owner

dsherret commented Jul 18, 2019

@nchanged that is a different issue not related to createSourceFile as that's after calling findReferences.

Could you open an issue with some reproduction steps? Most likely it's not resolving react properly and the compiler can't find the file in the ts.Program. Check if there are any diagnostics in project.getPreEmitDiagnostics().

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

No branches or pull requests

3 participants