Skip to content

Commit 57286a7

Browse files
author
Lenny Burdette
committed
feat(makeDefaultExport): add deprecation message for generated default exports
If a module does not define a default export, loader.js assigns `exports.default = exports` for compatibility with modules that don't use `_interopRequireDefault` from babel6. Mutating exports is unexpected behavior and causes difficult-to-diagnose [bugs][1]. We should remove `makeDefaultExport` in version 5.0. This update * adds a deprecation warning in preparation for removing `makeDefaultExport`. * adds a debug build with the deprecation enabled. Addresses ember-cli#114 [1]:mike-north/ember-lodash#104
1 parent 1268caa commit 57286a7

File tree

5 files changed

+133
-7
lines changed

5 files changed

+133
-7
lines changed

build.js

+22-5
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,37 @@ var transform = babel.transform;
55
var fs = require('fs');
66
var mkdirp = require('mkdirp').sync;
77

8+
var baseTransformPlugins = [
9+
'transform-es2015-destructuring',
10+
['babel-plugin-debug-macros', {
11+
envFlags: {
12+
source: 'env-flags',
13+
flags: { DEBUG: true }
14+
},
15+
debugTools: {
16+
source: 'debug-tools'
17+
}
18+
}]
19+
];
20+
821
mkdirp('./dist/loader');
922
var source = fs.readFileSync('./lib/loader/loader.js', 'utf8');
23+
1024
var instrumented = transform(source, {
11-
plugins: ['transform-es2015-destructuring']
25+
plugins: baseTransformPlugins
26+
}).code;
27+
28+
var debug = transform(source, {
29+
plugins: baseTransformPlugins
1230
}).code;
1331

1432
var stripped = transform(source, {
1533
// strip-heimdall *must* come before transpiling destructuring
1634
plugins: [
17-
'babel6-plugin-strip-heimdall',
18-
'transform-es2015-destructuring'
19-
],
35+
'babel6-plugin-strip-heimdall'
36+
].concat(baseTransformPlugins)
2037
}).code;
2138

2239
fs.writeFileSync('./dist/loader/loader.instrument.js', instrumented);
40+
fs.writeFileSync('./dist/loader/loader.debug.js', debug);
2341
fs.writeFileSync('./dist/loader/loader.js', stripped);
24-

lib/loader/loader.js

+36-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { DEBUG } from 'env-flags';
2+
13
var loader, define, requireModule, require, requirejs;
24

35
(function(global) {
@@ -112,12 +114,45 @@ var loader, define, requireModule, require, requirejs;
112114

113115
}
114116

117+
if (DEBUG) {
118+
var constructMakeDefaultExportDeprecationMessage = function(id) {
119+
return 'The `' + id + '` module does not define a default export, but loader.js ' +
120+
'generated one anyway. This behavior is deprecated and will be removed in v5.0.0. ' +
121+
'To opt out of this behavior, set `loader.makeDefaultExport = false`.';
122+
};
123+
124+
var makeDefaultExportDeprecationOptions = {
125+
until: '5.0.0',
126+
id: 'loaderjs.makeDefaultExport'
127+
};
128+
129+
loader.deprecationLogger = function() {
130+
console.warn.apply(console, arguments);
131+
};
132+
}
133+
115134
Module.prototype.makeDefaultExport = function() {
116135
var exports = this.module.exports;
117136
if (exports !== null &&
118137
(typeof exports === 'object' || typeof exports === 'function') &&
119138
exports['default'] === undefined && Object.isExtensible(exports)) {
120-
exports['default'] = exports;
139+
140+
if (DEBUG) {
141+
var id = this.id;
142+
143+
Object.defineProperty(exports, 'default', {
144+
get: function () {
145+
loader.deprecationLogger(
146+
constructMakeDefaultExportDeprecationMessage(id),
147+
false,
148+
makeDefaultExportDeprecationOptions
149+
);
150+
return exports;
151+
}
152+
});
153+
} else {
154+
exports['default'] = exports;
155+
}
121156
}
122157
};
123158

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"devDependencies": {
1111
"ara": "0.0.3",
1212
"babel-core": "^6.25.0",
13+
"babel-plugin-debug-macros": "^0.1.10",
1314
"babel-plugin-transform-es2015-destructuring": "^6.23.0",
1415
"babel6-plugin-strip-heimdall": "^6.0.1",
1516
"heimdalljs": "^0.3.2",

tests/all.js

+67
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ test('has api', function() {
6060
strictEqual(define.amd, undefined);
6161
equal(typeof requirejs, 'function');
6262
equal(typeof requireModule, 'function');
63+
equal(typeof loader.deprecationLogger, 'function');
6364
});
6465

6566
test('no conflict mode', function() {
@@ -434,6 +435,12 @@ test('assigns default when makeDefaultExport option enabled', function() {
434435
test('doesn\'t assign default when makeDefaultExport option is disabled', function() {
435436
var _loaderMakeDefaultExport = loader.makeDefaultExport;
436437
loader.makeDefaultExport = false;
438+
439+
var deprecationArguments;
440+
loader.deprecationLogger = function() {
441+
deprecationArguments = [].slice.call(arguments);
442+
};
443+
437444
var theObject = {};
438445
define('foo', ['require', 'exports', 'module'], function() {
439446
return theObject;
@@ -456,6 +463,8 @@ test('doesn\'t assign default when makeDefaultExport option is disabled', functi
456463
pendingQueueLength: 1
457464
});
458465

466+
strictEqual(deprecationArguments, undefined);
467+
459468
// clean up
460469
loader.makeDefaultExport = _loaderMakeDefaultExport;
461470
});
@@ -724,6 +733,11 @@ test('if factory returns a value it is used as export', function() {
724733
});
725734

726735
test('if a module has no default property assume the return is the default', function() {
736+
var deprecationArguments;
737+
loader.deprecationLogger = function() {
738+
deprecationArguments = [].slice.call(arguments);
739+
};
740+
727741
define('foo', [], function() {
728742
return {
729743
bar: 'bar'
@@ -748,10 +762,21 @@ test('if a module has no default property assume the return is the default', fun
748762
});
749763

750764
equal(foo.bar, 'bar');
765+
766+
deepEqual(deprecationArguments, [
767+
'The `foo` module does not define a default export, but loader.js generated one anyway. This behavior is deprecated and will be removed in v5.0.0. To opt out of this behavior, set `loader.makeDefaultExport = false`.',
768+
false,
769+
{ id: 'loaderjs.makeDefaultExport', until: '5.0.0' }
770+
]);
751771
});
752772

753773

754774
test('if a CJS style module has no default export assume module.exports is the default', function() {
775+
var deprecationArguments;
776+
loader.deprecationLogger = function() {
777+
deprecationArguments = [].slice.call(arguments);
778+
};
779+
755780
define('Foo', ['require', 'exports', 'module'], function(require, exports, module) {
756781
module.exports = function Foo() {
757782
this.bar = 'bar';
@@ -776,10 +801,21 @@ test('if a CJS style module has no default export assume module.exports is the d
776801
resolveRelative: 0,
777802
pendingQueueLength: 1
778803
});
804+
805+
deepEqual(deprecationArguments, [
806+
'The `Foo` module does not define a default export, but loader.js generated one anyway. This behavior is deprecated and will be removed in v5.0.0. To opt out of this behavior, set `loader.makeDefaultExport = false`.',
807+
false,
808+
{ id: 'loaderjs.makeDefaultExport', until: '5.0.0' }
809+
]);
779810
});
780811

781812

782813
test('if a module has no default property assume its export is default (function)', function() {
814+
var deprecationArguments;
815+
loader.deprecationLogger = function() {
816+
deprecationArguments = [].slice.call(arguments);
817+
};
818+
783819
var theFunction = function theFunction() {};
784820
define('foo', ['require', 'exports', 'module'], function() {
785821
return theFunction;
@@ -802,9 +838,20 @@ test('if a module has no default property assume its export is default (function
802838
resolveRelative: 0,
803839
pendingQueueLength: 1
804840
});
841+
842+
deepEqual(deprecationArguments, [
843+
'The `foo` module does not define a default export, but loader.js generated one anyway. This behavior is deprecated and will be removed in v5.0.0. To opt out of this behavior, set `loader.makeDefaultExport = false`.',
844+
false,
845+
{ id: 'loaderjs.makeDefaultExport', until: '5.0.0' }
846+
]);
805847
});
806848

807849
test('if a module has no default property assume its export is default (object)', function() {
850+
var deprecationArguments;
851+
loader.deprecationLogger = function() {
852+
deprecationArguments = [].slice.call(arguments);
853+
};
854+
808855
var theObject = {};
809856
define('foo', ['require', 'exports', 'module'], function() {
810857
return theObject;
@@ -827,9 +874,20 @@ test('if a module has no default property assume its export is default (object)'
827874
resolveRelative: 0,
828875
pendingQueueLength: 1
829876
});
877+
878+
deepEqual(deprecationArguments, [
879+
'The `foo` module does not define a default export, but loader.js generated one anyway. This behavior is deprecated and will be removed in v5.0.0. To opt out of this behavior, set `loader.makeDefaultExport = false`.',
880+
false,
881+
{ id: 'loaderjs.makeDefaultExport', until: '5.0.0' }
882+
]);
830883
});
831884

832885
test('does not add default if export is frozen', function() {
886+
var deprecationArguments;
887+
loader.deprecationLogger = function() {
888+
deprecationArguments = [].slice.call(arguments);
889+
};
890+
833891
var theObject = Object.freeze({});
834892
define('foo', ['require', 'exports', 'module'], function() {
835893
return theObject;
@@ -852,9 +910,16 @@ test('does not add default if export is frozen', function() {
852910
resolveRelative: 0,
853911
pendingQueueLength: 1
854912
});
913+
914+
strictEqual(deprecationArguments, undefined);
855915
});
856916

857917
test('does not add default if export is sealed', function() {
918+
var deprecationArguments;
919+
loader.deprecationLogger = function() {
920+
deprecationArguments = [].slice.call(arguments);
921+
};
922+
858923
var theObject = Object.seal({ derp: {} });
859924
define('foo', ['require', 'exports', 'module'], function() {
860925
return theObject;
@@ -877,6 +942,8 @@ test('does not add default if export is sealed', function() {
877942
resolveRelative: 0,
878943
pendingQueueLength: 1
879944
});
945+
946+
strictEqual(deprecationArguments, undefined);
880947
});
881948

882949
test('has good error message for missing module', function() {

yarn.lock

+7-1
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,12 @@ babel-plugin-dead-code-elimination@^1.0.2:
230230
version "1.0.2"
231231
resolved "https://registry.yarnpkg.com/babel-plugin-dead-code-elimination/-/babel-plugin-dead-code-elimination-1.0.2.tgz#5f7c451274dcd7cccdbfbb3e0b85dd28121f0f65"
232232

233+
babel-plugin-debug-macros@^0.1.10:
234+
version "0.1.10"
235+
resolved "https://registry.yarnpkg.com/babel-plugin-debug-macros/-/babel-plugin-debug-macros-0.1.10.tgz#dd077ad6e1cc0a8f9bbc6405c561392ebfc9a01c"
236+
dependencies:
237+
semver "^5.3.0"
238+
233239
babel-plugin-eval@^1.0.1:
234240
version "1.0.1"
235241
resolved "https://registry.yarnpkg.com/babel-plugin-eval/-/babel-plugin-eval-1.0.1.tgz#a2faed25ce6be69ade4bfec263f70169195950da"
@@ -1962,7 +1968,7 @@ rsvp@~3.2.1:
19621968
version "3.2.1"
19631969
resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-3.2.1.tgz#07cb4a5df25add9e826ebc67dcc9fd89db27d84a"
19641970

1965-
semver@^5.1.0:
1971+
semver@^5.1.0, semver@^5.3.0:
19661972
version "5.3.0"
19671973
resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
19681974

0 commit comments

Comments
 (0)