Repository: jshint/jshint
Pull request #2198 (2015-02-26 23:14:23)

[[Fix]] export default * are defined in lexical module scope. Fixes gh-2197
cc @jugglinmike @caitp 

- `class default function Identifier() {}`
- `class default class Identifier() {}`
- `class default class Identifier extends Heritage() {}`

In all cases, there is a lexical binding, `Identifier`, created.


Patch:
diff --git a/src/jshint.js b/src/jshint.js
index ee302c54..3575c726 100644
--- a/src/jshint.js
+++ b/src/jshint.js
@@ -3561,12 +3561,13 @@ var JSHINT = (function() {
     return classdef.call(this, true);
   });
 
-  function classdef(stmt) {
+  function classdef(isStatement) {
+
     /*jshint validthis:true */
     if (!state.option.inESNext()) {
       warning("W104", state.tokens.curr, "class");
     }
-    if (stmt) {
+    if (isStatement) {
       // BindingIdentifier
       this.name = identifier();
       addlabel(this.name, { type: "unused", token: state.tokens.curr });
@@ -4423,6 +4424,9 @@ var JSHINT = (function() {
 
   stmt("export", function() {
     var ok = true;
+    var token;
+    var identifier;
+
     if (!state.option.inESNext()) {
       warning("W119", state.tokens.curr, "export");
       ok = false;
@@ -4449,7 +4453,20 @@ var JSHINT = (function() {
       if (state.tokens.next.id === "function" || state.tokens.next.id === "class") {
         this.block = true;
       }
-      this.exportee = expression(10);
+
+      token = peek();
+
+      expression(10);
+
+      if (state.tokens.next.id === "class") {
+        identifier = token.name;
+      } else {
+        identifier = token.value;
+      }
+
+      addlabel(identifier, {
+        type: "function", token: token
+      });
 
       return this;
     }


Test patch:
diff --git a/tests/unit/core.js b/tests/unit/core.js
index 83b8fc2b..c9963117 100644
--- a/tests/unit/core.js
+++ b/tests/unit/core.js
@@ -734,6 +734,29 @@ exports.testES6ModulesNamedExportsAffectUnused = function (test) {
   test.done();
 };
 
+
+exports["class declaration export (default)"] = function (test) {
+  var source = fs.readFileSync(__dirname + "/fixtures/class-declaration.js", "utf8");
+
+  TestRun(test).test(source, {
+    esnext: true,
+    undef: true
+  });
+
+  test.done();
+};
+
+exports["function declaration export (default)"] = function (test) {
+  var source = fs.readFileSync(__dirname + "/fixtures/function-declaration.js", "utf8");
+
+  TestRun(test).test(source, {
+    esnext: true,
+    undef: true
+  });
+
+  test.done();
+};
+
 exports.testES6ModulesNamedExportsAffectUndef = function (test) {
   // The identifier "foo" is expected to have been defined in the scope
   // of this file in order to be exported.
diff --git a/tests/unit/fixtures/class-declaration.js b/tests/unit/fixtures/class-declaration.js
new file mode 100644
index 00000000..5a1f678c
--- /dev/null
+++ b/tests/unit/fixtures/class-declaration.js
@@ -0,0 +1,15 @@
+class Section {
+  constructor() {
+
+  }
+}
+
+export default class Header extends Section {
+  someMethod() {
+    // ...
+  }
+}
+
+Object.defineProperty(Header, "CONST", {
+  value: 1
+});
diff --git a/tests/unit/fixtures/function-declaration.js b/tests/unit/fixtures/function-declaration.js
new file mode 100644
index 00000000..3d0125b2
--- /dev/null
+++ b/tests/unit/fixtures/function-declaration.js
@@ -0,0 +1,5 @@
+export default function Header() {}
+
+Object.defineProperty(Header, "CONST", {
+  value: 1
+});