Skip to content

Commit 75d56f6

Browse files
author
Robert Jackson
authored
Merge pull request #170 from dwickern/ember-18047
2 parents 49b5b15 + 679c190 commit 75d56f6

4 files changed

Lines changed: 100 additions & 4 deletions

File tree

__tests__/index-test.js

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@ function transform7(source, _plugins) {
2323
return result.code;
2424
}
2525

26-
function transformWithPresetEnv(source) {
26+
function transformWithPresetEnv(source, _plugins) {
27+
let plugins = [].concat([[Plugin]], _plugins || []);
2728
let result = babel7.transformSync(source, {
28-
plugins: [[Plugin]],
29+
plugins,
30+
2931
presets: [['@babel/preset-env', { targets: { ie: '8' }, modules: false }]],
3032
});
3133

@@ -422,3 +424,36 @@ describe('when used with typescript', () => {
422424
expect(actual).toEqual(`Ember.addObserver();`);
423425
});
424426
});
427+
428+
describe('when used with native classes and decorators', () => {
429+
it('allows "action" to be used as a variable name', () => {
430+
let source = `
431+
import { action } from '@ember/object';
432+
import Controller from '@ember/controller';
433+
434+
export default class MyController extends Controller {
435+
@action
436+
addAction(action) {
437+
this.actions.pushObject(action);
438+
}
439+
}
440+
`;
441+
442+
let actual = transform7(source, [
443+
[Plugin],
444+
['@babel/plugin-proposal-decorators', { legacy: true }],
445+
]);
446+
447+
expect(actual).toEqual(`var _dec, _class;
448+
449+
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) { var desc = {}; Object.keys(descriptor).forEach(function (key) { desc[key] = descriptor[key]; }); desc.enumerable = !!desc.enumerable; desc.configurable = !!desc.configurable; if ('value' in desc || desc.initializer) { desc.writable = true; } desc = decorators.slice().reverse().reduce(function (desc, decorator) { return decorator(target, property, desc) || desc; }, desc); if (context && desc.initializer !== void 0) { desc.value = desc.initializer ? desc.initializer.call(context) : void 0; desc.initializer = undefined; } if (desc.initializer === void 0) { Object.defineProperty(target, property, desc); desc = null; } return desc; }
450+
451+
let MyController = (_dec = Ember._action, (_class = class MyController extends Ember.Controller {
452+
addAction(action) {
453+
this.actions.pushObject(action);
454+
}
455+
456+
}, (_applyDecoratedDescriptor(_class.prototype, "addAction", [_dec], Object.getOwnPropertyDescriptor(_class.prototype, "addAction"), _class.prototype)), _class));
457+
export { MyController as default };`);
458+
});
459+
});

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
},
3333
"devDependencies": {
3434
"@babel/core": "^7.12.10",
35+
"@babel/plugin-proposal-decorators": "^7.12.12",
3536
"@babel/plugin-transform-typescript": "^7.12.1",
3637
"@babel/preset-env": "^7.12.11",
3738
"babel-core": "^6.25.0",

src/index.js

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,24 @@ function isIgnored(ignore, importPath, exportName) {
1313
}
1414
}
1515

16+
function isDecorator(moduleName, importName) {
17+
switch (moduleName) {
18+
case '@ember/service':
19+
return importName === 'inject';
20+
case '@ember/controller':
21+
return importName === 'inject';
22+
case '@glimmer/tracking':
23+
return importName === 'tracked';
24+
case '@ember/object/compat':
25+
return importName === 'dependentKeyCompat';
26+
case '@ember/object':
27+
return ['action', 'computed'].includes(importName);
28+
case '@ember/object/computed':
29+
// only the default import of this module is not a decorator
30+
return importName !== 'default';
31+
}
32+
}
33+
1634
module.exports = function (babel) {
1735
const t = babel.types;
1836

@@ -165,10 +183,36 @@ module.exports = function (babel) {
165183
])
166184
);
167185
} else {
168-
// Replace the occurences of the imported name with the global name.
169186
let binding = path.scope.getBinding(local.name);
187+
let referencePaths = binding.referencePaths;
188+
189+
if (isDecorator(importPath, importName)) {
190+
// tldr; decorator paths are not always included in `path.scope.getBinding(local.name)`
191+
//
192+
// In some circumstances, decorators are not included in the
193+
// reference paths for a local binding when the decorator
194+
// identifier name is also defined _within_ the method being
195+
// decorated. This is likely a bug in Babel, that should be
196+
// reported and fixed.
197+
//
198+
// in order to fix that, we have to manually traverse to gather
199+
// the decorator references **before** the
200+
// @babel/plugin-proposal-decorators runs (because it removes
201+
// them)
202+
path.parentPath.traverse({
203+
Decorator(decoratorPath) {
204+
if (
205+
decoratorPath.node.expression.type === 'Identifier' &&
206+
decoratorPath.node.expression.name === local.name
207+
) {
208+
referencePaths.push(decoratorPath.get('expression'));
209+
}
210+
},
211+
});
212+
}
170213

171-
binding.referencePaths.forEach((referencePath) => {
214+
// Replace the occurences of the imported name with the global name.
215+
referencePaths.forEach((referencePath) => {
172216
if (!isTypescriptNode(referencePath.parentPath)) {
173217
referencePath.replaceWith(getMemberExpressionFor(global));
174218
}

yarn.lock

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,15 @@
277277
"@babel/helper-create-class-features-plugin" "^7.12.1"
278278
"@babel/helper-plugin-utils" "^7.10.4"
279279

280+
"@babel/plugin-proposal-decorators@^7.12.12":
281+
version "7.12.12"
282+
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.12.12.tgz#067a6d3d6ca86d54cf56bb183239199c20daeafe"
283+
integrity sha512-fhkE9lJYpw2mjHelBpM2zCbaA11aov2GJs7q4cFaXNrWx0H3bW58H9Esy2rdtYOghFBEYUDRIpvlgi+ZD+AvvQ==
284+
dependencies:
285+
"@babel/helper-create-class-features-plugin" "^7.12.1"
286+
"@babel/helper-plugin-utils" "^7.10.4"
287+
"@babel/plugin-syntax-decorators" "^7.12.1"
288+
280289
"@babel/plugin-proposal-dynamic-import@^7.12.1":
281290
version "7.12.1"
282291
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.12.1.tgz#43eb5c2a3487ecd98c5c8ea8b5fdb69a2749b2dc"
@@ -381,6 +390,13 @@
381390
dependencies:
382391
"@babel/helper-plugin-utils" "^7.10.4"
383392

393+
"@babel/plugin-syntax-decorators@^7.12.1":
394+
version "7.12.1"
395+
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.12.1.tgz#81a8b535b284476c41be6de06853a8802b98c5dd"
396+
integrity sha512-ir9YW5daRrTYiy9UJ2TzdNIJEZu8KclVzDcfSt4iEmOtwQ4llPtWInNKJyKnVXp1vE4bbVd5S31M/im3mYMO1w==
397+
dependencies:
398+
"@babel/helper-plugin-utils" "^7.10.4"
399+
384400
"@babel/plugin-syntax-dynamic-import@^7.8.0":
385401
version "7.8.3"
386402
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3"

0 commit comments

Comments
 (0)