-
-
Notifications
You must be signed in to change notification settings - Fork 255
Point out when a plugin needs to be enabled for encountered syntax (#169) #178
Conversation
|
Codecov Report
@@ Coverage Diff @@
## master #178 +/- ##
=========================================
Coverage ? 96.28%
=========================================
Files ? 20
Lines ? 3205
Branches ? 836
=========================================
Hits ? 3086
Misses ? 61
Partials ? 58
Continue to review full report at Codecov.
|
# Conflicts: # test/fixtures/experimental/no-async-generators/error-without-plugin/options.json
#182 will probably take care of most of the gap in code coverage |
Thanks @danez! Do you have an opinion on |
I think this would also be the only way to do it in this cases right? Because otherwise doing |
Yeah. The thing to watch out for, if we eagerly |
True, so probably we need to decide for each occurrence separately. |
OK, I'll just add some of the easy cases here real quick. |
this.state.inFunction = oldInFunction; | ||
this.state.labels = oldLabels; | ||
return this.finishNode(node, "DoExpression"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We had a fall-through here before without doExpression
, but that looks like a mistake anyway. Why should do fall-through to regexp?
this.expectPlugin("functionSent"); | ||
} else if (!this.hasPlugin("functionSent")) { | ||
// They didn't actually say `function.sent`, just `function.`, so a simple error would be less confusing. | ||
this.unexpected(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Uncovered. We could test this I think easily.
src/parser/statement.js
Outdated
if (this.hasPlugin("asyncGenerators") && this.state.inAsync && this.isContextual("await")) { | ||
forAwait = true; | ||
this.next(); | ||
if (this.isContextual("await") && this.state.inAsync) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe switch the order here like it was before, checking the bool value first is maybe also faster in case it is false?
Or did you have something in mind by changing the order.
if (this.hasPlugin("asyncGenerators") && this.eat(tt.star)) isGenerator = true; | ||
if (this.match(tt.star)) { | ||
this.expectPlugin("asyncGenerators"); | ||
this.next(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have this pattern quite often if (match) expectPlugin(), next()
So maybe we could make this easier in future by doing:
if (this.expectPluginIfMatch("plugin", tt.start)) {
is = true
}
What do you think?
But should do that anyway later, to not make this PR even more bigger.
if (pluginInfo.babelSyntaxPlugin) { | ||
pointers.push(`babel-plugin-${pluginInfo.babelSyntaxPlugin}`); | ||
} | ||
if (pluginInfo.babelTransformPlugins) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we make babelTransformPlugins
mandatory to be an array, so we don't have to check here?
And also as long as we have syntax-plugins for everything, maybe also remove the if for it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah we do have syntax plugins for every transform (otherwise we would just remove the syntax plugins entirely)
|
||
export const asyncGenerators = { | ||
babelSyntaxPlugin: "syntax-async-generators", | ||
babelTransformPlugins: ["transform-regenerator"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should recommend the new transform, transform-async-generators
|
||
export const classConstructorCall = { | ||
babelSyntaxPlugin: "syntax-class-constructor-call", | ||
babelTransformPlugins: ["transform-class-constructor-call"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is deprecated so can remove
babelTransformPlugins: ["transform-decorators"] | ||
}; | ||
|
||
// export const dynamicImport = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this can be added back
|
||
export const decorators = { | ||
babelSyntaxPlugin: "syntax-decorators", | ||
babelTransformPlugins: ["transform-decorators"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i guess recommend transform-legacy atm?
const leadingPointersOr = pointers.length ? ` ${pointers.join(", ")} or ` : " "; | ||
|
||
this.unexpected(null, `This experimental syntax requires the ${name} parser plugin. ` | ||
+ `Check out${ leadingPointersOr }the parserOpts.${name} Babel option.`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's an array so the wording is off (maybe we shouldn't recommend that option).
{
parserOpts: {
plugins: []
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should change it so that Babel users will use the transform plugin, and babylon users the syntax, and for Babel users not to use both.
@hzoo so basically - an enhanced error object, whose shape will be part of
Babylon's public API, and which we will then detect and translate on the
Babel side. Right?
My choice for this would either be an ad-hoc extra property e.g.
`missingBabylonPlugin` (and duck typing on that in Babel) or just full
blown VError with its first-class support for metadata. What do you think?
I'll only have my computer back from repairs next week but let's maybe hash
out the details here until I can write actual code.
…On Dec 15, 2016 23:36, "Henry Zhu" ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In src/parser/util.js <#178>:
> + const pluginInfo = plugins[name];
+ const pointers = [];
+ if (pluginInfo) {
+ if (pluginInfo.babelSyntaxPlugin) {
+ pointers.push(`babel-plugin-${pluginInfo.babelSyntaxPlugin}`);
+ }
+ if (pluginInfo.babelTransformPlugins) {
+ pointers.push(...pluginInfo.babelTransformPlugins.map(
+ (shortName) => `babel-plugin-${shortName}`
+ ));
+ }
+ }
+ const leadingPointersOr = pointers.length ? ` ${pointers.join(", ")} or ` : " ";
+
+ this.unexpected(null, `This experimental syntax requires the ${name} parser plugin. `
+ + `Check out${ leadingPointersOr }the parserOpts.${name} Babel option.`);
We should change it so that Babel users will use the transform plugin, and
babylon users the syntax, and for Babel users not to use both.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#178>, or mute the thread
<https://github.com/notifications/unsubscribe-auth/ACJHpRgemrtssWZrqvq6Q8_RHqzbVjoLks5rIbLWgaJpZM4KXfLp>
.
|
Hmm yeah it would be interesting to just expose metadata that says the babylon plugin that could be applied to not error. 2 users of babylon: either a tool or a Babel user. I would focus on the Babel user since the tool user would be more likely to understand how to enable a plugin anyway? |
Btw just resolved the obvious conflicts for now. Will do a more complete update soon. |
@@ -0,0 +1,59 @@ | |||
export const classProperties = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking about this again...
most if not all of the plugins have a corresponding babel plugin that is just the same name.
objectRestSpread -> transform-object-rest-spread
asyncGenerators -> transform-async-generators
the only 2 that don't do that have a corresponding preset as well now
flow -> preset-flow (strip-flow-types)
react -> preset-react (react-jsx)
So we could do this automatically + 2 exceptions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most is right. There's also the question of transform
vs syntax
(even if we ignore the rarer preset
and ...-legacy
etc) which is still an editorial decision basically
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-legacy will be removed (there's a pr to make it the regular one) babel/babel#5283
and ^ I said we always have a transform/syntax version so far at least
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yeah, sorry, I was commenting with just a fuzzy recollection of how my own PR worked 🤦♂️ . So transform
vs syntax
is accounted for, by way of both being mentioned in the message.
And now I see your edit above. So yeah, that's a way to encode the same information less explicitly. However I would still kinda prefer to move this out of Babylon if possible, as discussed below.
@hzoo I read "focus on the Babel user" to mean a relaxing of your suggestion above that
So that - showing only the Babel oriented message - would be one simple way to slice it, albeit one that sacrifices some "purity" I guess by coding knowledge about Babel plugins into Babylon (funnily enough, I would be more comfortable with this philosophically if Babylon was in the Babel monorepo... Ooh can we get that in 7.0? :half_joking:) |
Yeah I've wanted that for a while now (doesn't have to be in 7.0 either but based on time probably so unless someone really wants to do it). Although I guess it would be a weird error message if the user isn't using Babel but Babylon itself |
Yep, that's what I'm least happy about here. I totally get the "focus on Babel users" argument, but Babylon is its own package too and I don't love the idea of making that kind of a second-class story. It's a subtle point though. To expand on the alternative: It's more work, but if we make the "plugin required" exception positively identifiable to Babel 1, e.g. via a custom property on the thrown object 2, then we can both
1: Or anyone invoking Babylon - i.e this becomes public API. |
this.unexpected(null, `This experimental syntax requires the ${name} parser plugin. ` | ||
+ `Check out${ leadingPointersOr }the parserOpts.${name} Babel option.`); | ||
} | ||
return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason to return from this given that the alternative is to throw? Then we could reduce the nesting by just doing if (this.hasPlugin(name)) return;
at the top of the function.
)); | ||
} | ||
} | ||
const leadingPointersOr = pointers.length ? ` ${pointers.join(", ")} or ` : " "; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How would people feel about skipping this pointers
logic and having babylon
just throw an error with the plugin name in the message, and a .missingPlugin
property?
They we can have the try/catch in babel-core
catch the exception and handle mapping the error to a Babel plugin? That way we don't need Babylon to be aware of Babel stuff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that was my thoughts dono if it was in the notes. Babylon would just throw and babel-core would pick that up and throw the real error message since you might be using babylon for something else (prettier, babel-eslint) that is unrelated to using a plugin
Ref #169
This mainly replaces the
if (!this.hasPlugin("foo")) this.unexpected();
idiom with a new parser helper method,expectPlugin
.Right now the error is worded like this:
This experimental syntax requires the foo parser plugin. Check out babel-plugin-syntax-foo, babel-plugin-transform-foo or the parserOpts.foo Babel option.
Open for different suggestions of course.Another idiom we may want to change is where we currently check for things like
this.hasPlugin("asyncGenerators") && this.eat(tt.star)
. Does it make sense to flip the order in some places? Match the special token (here*
) without advancing, then assert that we have the plugin, then advance. In cases where we can be sure of the user's intent this looks like a usability win.