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

CoffeeScript compiling badly interacts with Babel #7558

Merged
merged 8 commits into from
Oct 6, 2016

Conversation

mitar
Copy link
Contributor

@mitar mitar commented Aug 3, 2016

It seems there is a bad interaction with Babel and CoffeeScript. Recently, a change has been introduced to CoffeeScript compiling which allows Babel to process code inside backticks. The bad side-effect of this is that when Babel processes the code, it rewraps the list of defined variables, which breaks these assumptions. As a consequence, some exported variables cannot be defined because a local variable is defined.

The problem is even more problematic because even if a backtick is just inside a comment in the CoffeeScript (read: Markdown documentation) it triggers Babel processing, breaking exports.

This pull requests adds tests which break CoffeeScript in the above described way.

@mitar mitar added confirmed We want to fix or implement it Type:Bug Impact:few labels Aug 3, 2016
mitar added a commit to peerlibrary/meteor-blaze-common-component that referenced this pull request Aug 3, 2016
To address the Meteor CoffeeScript compilation issue
meteor/meteor#7558
@benjamn
Copy link
Contributor

benjamn commented Aug 16, 2016

As you probably realize, we can't merge this until it's fixed, though we do very much appreciate that you've provided these test cases (instead of just reporting the problem).

When we get around to fixing this, we'll open a pull request against mitar:coffeescript-babel, which you can review and merge, which will then update this PR, and hopefully the tests will pass here, and then we can merge this PR into devel. Sound good?

@mitar
Copy link
Contributor Author

mitar commented Aug 16, 2016

I can also just move the branch to this repository?

@mitar
Copy link
Contributor Author

mitar commented Aug 16, 2016

Or I can give permissions on that fork for whoever wants. But yes, pull request is also good.

@GeoffreyBooth
Copy link
Contributor

Alternatively, now that CoffeeScript supports import and export, people could use that to share objects between files. Dropping support for shared variables would be a breaking change, though.

@GeoffreyBooth
Copy link
Contributor

@benjamn I think this is actually a bug in the Babel compiler. So from this CoffeeScript file, with no backticks or modules and therefore no Babel postprocessing:

COFFEESCRIPT_EXPORTED = 123
COFFEESCRIPT_EXPORTED_ONE_MORE = 234
COFFEESCRIPT_EXPORTED_WITH_BACKTICKS = 345
class TestClass
class ClassExtending extends TestClass

we get this:

__coffeescriptShare = typeof __coffeescriptShare === 'object' ? __coffeescriptShare : {}; var share = __coffeescriptShare;
var ClassExtending, TestClass,                                                                                             
  extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
  hasProp = {}.hasOwnProperty;                                          //
                                                                        //
COFFEESCRIPT_EXPORTED = 123;                                            // 1
                                                                        //
COFFEESCRIPT_EXPORTED_ONE_MORE = 234;                                   // 2
                                                                        //
COFFEESCRIPT_EXPORTED_WITH_BACKTICKS = 345;                             // 6
                                                                        //
TestClass = (function() {                                               // 13
  function TestClass() {}                                               //
                                                                        //
  return TestClass;                                                     //
                                                                        //
})();                                                                   //
                                                                        //
ClassExtending = (function(superClass) {                                // 15
  extend(ClassExtending, superClass);                                   //
                                                                        //
  function ClassExtending() {                                           //
    return ClassExtending.__super__.constructor.apply(this, arguments);
  }                                                                     //
                                                                        //
  return ClassExtending;                                                //
                                                                        //
})(TestClass);                                                          //

which works fine. Wrap the third line in backticks, though, to trigger Babel, and you get this:

__coffeescriptShare = typeof __coffeescriptShare === 'object' ? __coffeescriptShare : {}; var share = __coffeescriptShare;
var                                                                                                                //
    COFFEESCRIPT_EXPORTED_ONE_MORE,                                                                                //
    ClassExtending,                                                                                                //
    TestClass,                                                                                                     //
    extend = function extend(child, parent) {                                                                      //
  for (var key in meteorBabelHelpers.sanitizeForInObject(parent)) {                                                //
    if (hasProp.call(parent, key)) child[key] = parent[key];                                                       //
  }function ctor() {                                                                                               //
    this.constructor = child;                                                                                      //
  }ctor.prototype = parent.prototype;child.prototype = new ctor();child.__super__ = parent.prototype;return child;
},                                                                                                                 //
    hasProp = {}.hasOwnProperty;                                                                                   //
                                                                                                                   //
COFFEESCRIPT_EXPORTED = 123;                                                                                       //
                                                                                                                   //
COFFEESCRIPT_EXPORTED_ONE_MORE = 234;                                                                              //
                                                                                                                   //
COFFEESCRIPT_EXPORTED_WITH_BACKTICKS = 345;                                                                        //
                                                                                                                   //
TestClass = function () {                                                                                          //
  function TestClass() {}                                                                                          //
                                                                                                                   //
  return TestClass;                                                                                                //
}();                                                                                                               //
                                                                                                                   //
ClassExtending = function (superClass) {                                                                           //
  extend(ClassExtending, superClass);                                                                              //
                                                                                                                   //
  function ClassExtending() {                                                                                      //
    return ClassExtending.__super__.constructor.apply(this, arguments);                                            //
  }                                                                                                                //
                                                                                                                   //
  return ClassExtending;                                                                                           //
}(TestClass);                                                                                                      //

The important thing to note here is that COFFEESCRIPT_EXPORTED_ONE_MORE is added to the var statement (line 3 in the latter JavaScript block). This is just completely wrong, and this is Babel’s doing (or the doing of whatever Meteor package wraps it). It’s all the more perplexing because for whatever reason the other COFFEESCRIPT_EXPORTED variables don’t receive the same treatment. It’s triggered by the class extends; take out the second class statement, and the bug goes away.

Do you know where the code is that generates this var statement?

…ugh the Babel compiler, to avoid Babel turning a one-line `var` statement into a multiline `var` statement and breaking the parsing logic in `stripExportedVars`
@GeoffreyBooth
Copy link
Contributor

@benjamn I take it back. It’s not Babel’s fault. It’s compile-coffeescript.js’ stripExportedVars.

Or more specifically, it’s the fact that stripExportedVars was written expecting the output from the CoffeeScript compiler, not the output from the Babel compiler. CoffeeScript creates a var line near the top that is a long one-liner: var a, b, c;. Babel converts such a line to a multiline construction:

var a,
  b,
  c;

Or at least it does in the case of @mitar’s tests.

Rather than rewrite stripExportedVars to handle a multiline var statement, we can just reorder the steps so that stripExportedVars happens before the Babel compilation. So now it’s CoffeeScript compilation, then stripExportedVars, then Babel compilation. This new order passes @mitar’s tests.

I submitted the fix as mitar#1, per @benjamn’s instructions above. @mitar, after you accept the PR you should merge the latest upstream devel into your branch, to incorporate the updates from #7818. It might trigger a mild merge conflict, which should be easy to resolve.

mitar and others added 2 commits September 30, 2016 23:13
@mitar
Copy link
Contributor Author

mitar commented Oct 1, 2016

Merged, updated, merged, and tests are running.

@mitar
Copy link
Contributor Author

mitar commented Oct 2, 2016

Updated once more.

@GeoffreyBooth
Copy link
Contributor

@benjamn I think this should be ready for review.

Copy link
Contributor

@benjamn benjamn left a comment

Choose a reason for hiding this comment

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

Great work @mitar and @GeoffreyBooth!

@benjamn benjamn merged commit 2b1a33e into meteor:devel Oct 6, 2016
@mitar
Copy link
Contributor Author

mitar commented Oct 6, 2016

Can you bump the coffeescript Meteor package version please? ;-)

See my comment here: 8dbfd97

@@ -1,14 +1,14 @@
Package.describe({
summary: "Javascript dialect with fewer braces and semicolons",
version: "1.2.6"
version: "1.2.7"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can we make this 1.11.1_1?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
confirmed We want to fix or implement it Impact:few Type:Bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants