In recent months, JSHint has been receiving requests to support proposed
JavaScript language features like
async
/await
, method
decorators, and class
property declarations
[1]. The JSHint team has turned down each request, even after the
release of ES2015. This has
not been easy; as open source project maintainers, nothing gives us more
pleasure than to write code in benefit of our users. We've tried to explain our
motivations in an ad-hoc way, but it's always come off as a bit haphazard. We'd
like to take some time to more thoroughly describe our thought process.
The long and short of it is: JSHint will require all language extension proposals to be at "Stage 2" of TC-39's standardization process before parsing and linting them.
Projects like Traceur and Babel have become very popular through progressive implementation of the latest features and proposals. It's fair to question why JSHint should be any different.
First off, it's difficult for the project maintainers. This is not really a defensible motivation; we only offer it in the interest of full disclosure. Over the years, JSHint has grown from JSLint thanks to the efforts of hundreds of contributors. This has been key to its success, but it has also contributed to a fair amount of technical debt. Extending the parser requires a fair amount of study, and even then, it isn't always clear how to do this cleanly.
The project has tried to be progressive about new syntax in the past
[2], and this has often contributed to technical debt. For instance,
JSHint continues to
(grudgingly)
maintain a moz
option for Mozilla-specific
extensions,
and the current esnext
option includes non-standard array comprehensions. (By
the way: now that ES2015 is released, that name describes a non-existent
specification draft.)
A plugin system is one possible way to address this, but that will require a large effort involving careful design, spanning refactoring, and a long-term commitment to implementation details.
More importantly, it's hazardous for developers. Inconsistencies within toolchains will gate developers on the lowest common denominator. Imagine the day that your transpiler supports draft 17 but JSHint has moved to draft 18. Even if you're not struggling with coordination issues between parsers, the release cycle for a "progressive" parser would leave most application developers behind. Projects would frequently rely on outdated release channels that no longer received bug fixes or new features.
JSHint was born out of a reluctance to make decisions on behalf of the user, so while we think the above considerations should be made clear to JSHint's users, we haven't made this decision based on them.
We also believe it's harmful for the ecosystem. Empowering developers to write non-standard code can have long-term effects on the open source ecosystem. Code has a tendency to live longer than we expect, but it isn't always maintained throughout its lifetime. We all look forward to the day that features like method decorators are standard and widely-supported. Prior to that, it's important to remember that the code we write with experimental language features is itself non-standard. It will be frustrating if, in 2 years, you are reviewing older code that seems to use "standard" function decorators but that in reality depends on the syntax and semantics of "revision 12" of the function decorator proposal. The differences may be subtle, and you will be forced to research the behavior of this not-quite-JavaScript before you can contribute to the project.
Finally, it's unhealthy for the language. TC39 does not operate from an
ivory tower. They recognize practical considerations of patterns in existing
code. Look no further than Annex
B
for proof of that. Sometimes, that is a good thing. The Promises/A+
spec offered a clear way forward when Promises
were first considered for inclusion in ES2015. Sometimes, this is a bad thing.
It's the reason why we expect
Array.prototype.includes
instead of
Array.prototype.contains
.
The proliferation of production code can have a solidifying
effect
on new features. To the extent that JSHint is a channel through which new
JavaScript flows (a minuscule one to be sure), we want to cooperate with the
design process.
Stage 2 seems specifically designed for our position in the ecosystem.
It's not too early. Features in this stage have a formal definition (so we have a consistent and complete set of instructions to build from). These features are also relatively stable, so JSHint has a chance to keep up with the latest draft modifications.
It's also not too late. The design process can still benefit from the experience of developers applying a given pattern to their code. The process document defines "incremental" changes expected for Stage 2 proposals, and only recommends "experimental" implementations. You might say, "these features seem stable enough", but the truth is, JSHint was dealing with non-trivial modifications to ES2015 from the moment we implemented them until as late as May of this year [3]. So it's worth noting that JSHint is still taking some risk here.
We think there's tremendous value in experimentation at earlier stages in the process. We also feel that research should be conducted in non-production settings in order to avoid the more fundamental problems discussed above. Early-stage experiments have less to gain from automated code analysis both because of their limited scope and because their syntax is already verified by a "transpiler". In these contexts, linting has much more limited relevance.
Here's the practical effect of all that talk:
esnext
option; it will instead support
esversion: 6
.moz
option, and it will be the only
setting that enables array comprehension parsing.esversion: 7
option until that specification is
finalized by ECMA.experimental
configuration namespace. These features will be clearly documented as subject
to breaking changes between major releases.This policy means denying first-class support for non-standard and early
proposal features. JSHint will continue to support ignore
directives as a
coarse-grained mechanism for operating on non-standard and early-proposal
features, but we know other linting tools go much farther than
that. We're motivated by our
responsibility to developers but also to the open source ecosystem and to the
standards process itself. We hope JSHint's usership continues to enjoy the tool
because they share these same values.
esnext
) error: `import * as foo from
'bar'