Skip to content
This repository has been archived by the owner on Mar 25, 2021. It is now read-only.

Implement --stdin and --stdin-filename CLI options #3816

Closed
wants to merge 2 commits into from

Conversation

w0rp
Copy link

@w0rp w0rp commented Apr 6, 2018

PR checklist

Overview of change:

Hello! I am the guy who created the ALE plugin for Vim, for checking for problems while you type in Vim, and I am an active JavaScript and TypeScript developer. I use ESLint in my day job, and I would like to use TSLint more. One of the key features that ESLint has, which works really well with ALE, is that you can both pass in a Vim buffer via stdin and also tell it where the file for the buffer is in your file system. This means you can check for problems before you've saved a file to disk, but still benefit from plugins which need to know where the file is for resolving modules and so on. Currently, ALE can't support all of the features of TSLint, while also checking for problems while you type, because TSLint doesn't yet support a feature like this.

I'm interested in using such a feature myself, so I thought I'd start trying to implement it myself. A new --stdin option ought to read a file to check from stdin, and --stdin-filename should tell TSLint where the file you're checking will be saved to disk.

Is there anything you'd like reviewers to focus on?

I'm not familiar with the TSLint codebase at all. I just quickly scanned through the codebase and modified what seemed logical to modify to me, to get anything working at all. I haven't written any tests yet, and I'm sure what I've written so far will probably cause some bugs. I'm not familiar with the coding standards, so let me know what to change there. This work certainly isn't finished yet.

One thing I know that I couldn't get working yet is that I couldn't figure out an easy way to support --stdin without the --stdin-filename option, as some rules seem to depend on there being some file to actually look at in the filesystem, and I imagine this will extend to some third party rules as well. It might be that some rules will have to be modified to not throw exceptions if the file doesn't exist yet, they will probably cause problems for new files you haven't saved to disk yet, even with the --stdin-filename option. Maybe for supporting --stdin alone, rules will have to be modified to tolerate blank filenames, or a special filename like "-". Let me know your thoughts.

CHANGELOG.md entry:

suggested tags: [enhancement], [api]

@palantirtech
Copy link
Member

Thanks for your interest in palantir/tslint, @w0rp! Before we can accept your pull request, you need to sign our contributor license agreement - just visit https://cla.palantir.com/ and follow the instructions. Once you sign, I'll automatically update this pull request.

@w0rp
Copy link
Author

w0rp commented Apr 6, 2018

Also, fixing code via --stdin should be considered, but I'd probably focus on getting checking for errors implemented first, and implement fixing problems automatically second. It's worth keeping it in mind while working on this.

@w0rp
Copy link
Author

w0rp commented Apr 23, 2018

I'd appreciate some feedback on this if anyone wants to take a look. I imagine quite a bit more work will need to be done before this is good enough.

Copy link

@jaalzateolaya jaalzateolaya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes are required to pass tests

src/runner.ts Outdated
@@ -157,7 +167,9 @@ async function runWorker(options: Options, logger: Logger): Promise<Status> {
}

async function runLinter(options: Options, logger: Logger): Promise<LintResult> {
const { files, program } = resolveFilesAndProgram(options, logger);
const program = resolveProgram(options);
const files = await resolveFiles(options, program, logger);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You aren't returning a Promise here.

src/runner.ts Outdated
function resolveFilesAndProgram(
{ files, project, exclude, outputAbsolutePaths }: Options,
/** Read the stdin stream into a string with all of the contents. */
function readStdin(): Promise<string> {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The signature should be:

async function readStdin(): Promise<string>

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure.

console.error("No files specified. Use --project to lint a project folder.");
process.exit(1);
}

if (argv.stdin && !argv.stdinFilename) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

strict-boolean-expressions forces this expression to be:

if (arg.stdin && argv.stdinFilename !== undefined) {
// code...
}

@w0rp
Copy link
Author

w0rp commented Apr 24, 2018

Thanks for the feedback. I'd appreciate some in-depth feedback about how to handle just --stdin without --stdin-filename, what might go wrong, etc.

@jaalzateolaya
Copy link

Perhaps you should also change the subject of the commit, maybe something like:

"Implement --stdin and --stdin-filename CLI options"

@w0rp w0rp changed the title Experiment with --stdin and --stdin-filename support for tslint Implement --stdin and --stdin-filename CLI options Apr 24, 2018
@suchanlee
Copy link
Contributor

Hey @w0rp thank you for your contribution! Just wanted to let you know that I'll be looking into this PR probably sometimes next week (swamped with work this week). I think that this is really interesting and can be a very powerful feature.

@w0rp
Copy link
Author

w0rp commented Apr 26, 2018

Okay, sounds good. Thanks for letting me know. 👍

src/runner.ts Outdated

process.stdin.on("error", (error: Error) => {
reject(error);
});

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation is wrong throughout this file. some 4 spaces, some 2 spaces.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll fix that when I get the time. I haven't managed to get tslint running on tslint code itself yet.

src/runner.ts Outdated
stdin?: boolean;

/**
* Tell TSLint where the file for some stdin data is.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this language is awkward. what is "some stdin data?" please clarify this before merging.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Throw whatever string you want at me, and I'll write that.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about:

Tell TSLint where the actual file being linted through stdin is, if any.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, done. 👍

@w0rp
Copy link
Author

w0rp commented May 26, 2018

@suchanlee Will you have some time to look at this soon? I'm not familiar with the TSLint codebase at all, but I'm willing to do the work to get this feature done, with some guidance. I get reports of issues with TSLint integration with ALE pretty much every week.

src/runner.ts Outdated
stdin?: boolean;

/**
* Tell TSLint where the file for some stdin data is.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about:

Tell TSLint where the actual file being linted through stdin is, if any.

if (program !== undefined) {

if (resolvedFile.isStdin) {
contents = await readStdin();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is --std-filename being ignored when actually fetching content with --std?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Essentially yes. The filename is a hint for where the file is, and the data comes from stdin.

|| argv.project !== undefined
|| commander.args.length > 0
|| argv.stdin
)) {
console.error("No files specified. Use --project to lint a project folder.");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be a good idea to mention --stdin here

src/runner.ts Outdated
// Use a filename for shadowing stdin, if the option is set.
return [{filename: path.resolve(stdinFilename), isStdin: true}];
} else {
return [{filename: "", isStdin: true}];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe for now you should fail hard here with a good error message, until --stdin w/o --stdinFilename is supported

type: "string",
describe: "stdin input",
description: dedent`
Read and check a file via stdin.`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would mention that stdin-filename is currently required

@suchanlee
Copy link
Contributor

the way it currently stands, it looks like if the file(s) to lint is not specified, the program would fail at Linter.lint when getSourceFile is called: https://github.com/palantir/tslint/blob/master/src/linter.ts#L250

One way to hack around it is to create a ts.SourceFile object from the lint method when using --stdin using ts.createSourceFile (or the utils.getSourceFile wrapper), since we have the source available in the Linter.lint method and can generate something for the file name. And as long as we have a valid ts.SourceFile object, I dont think any of the rules will break. Some of them may malfunction though, esp noImplicitDependenciesRule, noReferenceImportRule, and noUnusedVariableRule (deprecated), but we might be able to do something about those as well, with the way we construct the ts.SourceFile.

I am also open to incrementally developing this, with the version that requires --stdin-filename first and then working through to support one that doesn't require the argument.

@w0rp
Copy link
Author

w0rp commented Jun 27, 2018

Thank you for the feedback. 👍 I'll have a read through all of your comments when I get the time. I was trying to figure out how to support --stdin without --stdin-filename. I agree that it might be a good idea to just require --stdin-filename at first, and add support for --stdin without --stdin-filename later.

@w0rp
Copy link
Author

w0rp commented Jul 7, 2018

I think I'll go with the option to require --stdin-filename at first, and someone can work on supporting --stdin without --stdin-filename later.

@w0rp
Copy link
Author

w0rp commented Jul 7, 2018

I updated the code now to require --stdin-filename for now. Let me know what else I can do. Perhaps there's some way I can cover some of this with tests. I haven't looked at the tests yet.

@blueyed
Copy link

blueyed commented Sep 9, 2018

@w0rp
There are some test failures - maybe you have a test there already that just needs to be adjusted?

@tommck
Copy link

tommck commented Oct 27, 2018

I'm waiting anxiously for this to be finished so I can integrate the tslint-cli into the Visual Studio Prettier extension.

Any status? Anything I can help with?

@w0rp
Copy link
Author

w0rp commented Oct 27, 2018

You can take what I've written so far and add to it. I don't need this any more. I just run tslint as a tsserver plugin instead.

Copy link
Contributor

@JoshuaKGoldberg JoshuaKGoldberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests will need to pass before this is merged.

function resolveFilesAndProgram(
{ files, project, exclude, outputAbsolutePaths }: Options,
/** Read the stdin stream into a string with all of the contents. */
async function readStdin(): Promise<string> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

runner.ts is getting pretty huge. Please separate this standalone utility to a different file (`src/utils.ts, maybe?).

@tommck
Copy link

tommck commented Nov 9, 2018

It's taking me a bit to get used to the code base, but I'll try to get to this. It won't be too soon though.

If anyone else wants to do it, it would be great. Just let me know.

@tommck
Copy link

tommck commented Nov 15, 2018

So, just to clarify what's needed now:

  • Need to extract the stdin reading into an external file (possibly src/utils.js)
  • Need to get unit tests passing.

Anything else?

@JoshuaKGoldberg
Copy link
Contributor

@tommck looks like it, yes. Also a few merge conflicts now 😉 but those are probably just from a recent Prettier reformatting.

@adidahiya
Copy link
Contributor

given what @w0rp said here:

I don't need this any more. I just run tslint as a tsserver plugin instead.

is this feature still required? what are the downsides to running tslint as a tsserver plugin?

@blueyed
Copy link

blueyed commented Jan 9, 2019

is this feature still required? what are the downsides to running tslint as a tsserver plugin?

I would still use it in Neomake I guess, which does not has support for tsserver plugins.
It is still useful for running tslint on its own.

@w0rp
Copy link
Author

w0rp commented Jan 9, 2019

I think it's worth implementing for various purposes, including piping text into TSLint. Someone else can pick this up. I personally recommend running TSLint as a tsserver plugin, as that seems to work very well.

@JoshuaKGoldberg
Copy link
Contributor

JoshuaKGoldberg commented Jun 15, 2019

Given #4534 and this PR's failing tests, merge conflicts, and lack of participation in the last few months, I'm going to go ahead and close this PR for housekeeping. Thanks so much everyone for participating in the PR! Although the changes haven't been merged in, there was still some great work that went into this. 😢

Anybody: if piping text via --stdin / --stdin-filename is still relevant to you, please file a separate PR that addresses the comments above.

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

Successfully merging this pull request may close these issues.

9 participants