Skip to content

Commit 61b457e

Browse files
author
Robert Jackson
committed
Refactor to fix up a few issues.
* Ensure import is only added if needed * Use `scope.generateUidIdentifier` to create the new local when needed * Ensure the identifier rewriting code uses the correct identifier
1 parent 6e2f2f8 commit 61b457e

2 files changed

Lines changed: 62 additions & 29 deletions

File tree

__tests__/index-test.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,10 +243,17 @@ describe('options', () => {
243243
});
244244

245245
describe('useEmberModule', () => {
246+
it('does not add Ember import when no Ember related imports are needed', () => {
247+
let input = `console.log('hi mom!');`;
248+
let actual = transform(input, [[Plugin, { useEmberModule: true }]]);
249+
250+
expect(actual).toEqual(input);
251+
});
252+
246253
it(`adds the ember import when used in sub-modules`, () => {
247254
let input = `import Component from '@ember/component';export default class extends Component {}`;
248255
let actual = transform(input, [[Plugin, { useEmberModule: true }]]);
249-
let expected = `import ${Plugin.uniqueishGlobalName} from 'ember';\nexport default class extends Ember.Component {}`;
256+
let expected = `import _Ember from 'ember';\nexport default class extends _Ember.Component {}`;
250257

251258
expect(actual).toEqual(expected);
252259
});
@@ -258,6 +265,14 @@ describe('options', () => {
258265
expect(actual).toEqual(input);
259266
});
260267

268+
it(`reuses a pre-existing ember import`, () => {
269+
let input = `import Ember from 'ember'; import Component from '@ember/component'; export default class extends Component {}`;
270+
let actual = transform(input, [[Plugin, { useEmberModule: true }]]);
271+
let expected = `import Ember from 'ember';export default class extends Ember.Component {}`;
272+
273+
expect(actual).toEqual(expected);
274+
});
275+
261276
it(`keeps the ember import when renamed`, () => {
262277
let input = `import BestFramework from 'ember';let x = BestFramework;`;
263278
let actual = transform(input, [[Plugin, { useEmberModule: true }]]);

src/index.js

Lines changed: 46 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,6 @@ function isDecorator(moduleName, importName) {
3131
}
3232
}
3333

34-
// what are the odds someone else defines this?
35-
const EmberGlobalImportName = '____EMBER_GLOBAL____';
36-
37-
function emberImport(t) {
38-
return t.importDeclaration(
39-
[t.importDefaultSpecifier(t.identifier(EmberGlobalImportName))],
40-
t.stringLiteral('ember')
41-
);
42-
}
43-
4434
module.exports = function (babel) {
4535
const t = babel.types;
4636

@@ -68,14 +58,17 @@ module.exports = function (babel) {
6858
reverseMapping[importRoot][importName] = imported;
6959
});
7060

71-
function getMemberExpressionFor(global) {
61+
function getMemberExpressionFor(global, emberIdentifier) {
7262
let parts = global.split('.');
7363

7464
let object = parts.shift();
7565
let property = parts.shift();
7666

67+
let objectIdentifier =
68+
object === 'Ember' ? emberIdentifier : t.identifier(object);
69+
7770
let memberExpression = t.MemberExpression(
78-
t.identifier(object),
71+
objectIdentifier,
7972
t.identifier(property)
8073
);
8174

@@ -96,23 +89,47 @@ module.exports = function (babel) {
9689
visitor: {
9790
Program(path, state) {
9891
let options = state.opts || {};
99-
let useEmberModule = options.useEmberModule || false;
100-
101-
if (!useEmberModule) return;
92+
let useEmberModule = Boolean(options.useEmberModule);
10293

103-
let hasEmberImport = path
94+
let preexistingEmberImportDeclaration = path
10495
.get('body')
10596
.filter((n) => n.type === 'ImportDeclaration')
10697
.find((n) => n.get('source').get('value').node === 'ember');
10798

108-
if (!hasEmberImport) {
109-
path.unshiftContainer('body', emberImport(t));
99+
if (
100+
// an import was found
101+
preexistingEmberImportDeclaration &&
102+
// this accounts for `import from 'ember'` without a local identifier
103+
preexistingEmberImportDeclaration.node.specifiers.length > 0
104+
) {
105+
state.emberIdentifier =
106+
preexistingEmberImportDeclaration.node.specifiers[0].local;
110107
}
108+
109+
state.ensureEmberImport = () => {
110+
if (!useEmberModule) {
111+
// ensures that we can always assume `state.emberIdentifier` is set
112+
state.emberIdentifier = t.identifier('Ember');
113+
return;
114+
}
115+
116+
if (state.emberIdentifier) return;
117+
118+
state.emberIdentifier = path.scope.generateUidIdentifier('Ember');
119+
120+
let emberImport = t.importDeclaration(
121+
[t.importDefaultSpecifier(state.emberIdentifier)],
122+
t.stringLiteral('ember')
123+
);
124+
125+
path.unshiftContainer('body', emberImport);
126+
};
111127
},
128+
112129
ImportDeclaration(path, state) {
113130
let options = state.opts || {};
114131
let ignore = options.ignore || [];
115-
let useEmberModule = options.useEmberModule || false;
132+
let useEmberModule = Boolean(options.useEmberModule);
116133
let node = path.node;
117134
let declarations = [];
118135
let removals = [];
@@ -133,11 +150,8 @@ module.exports = function (babel) {
133150
if (specifierPath) {
134151
let local = specifierPath.node.local;
135152

136-
if (useEmberModule) {
137-
if (local.name === 'Ember') {
138-
path.scope.rename(EmberGlobalImportName);
139-
}
140-
} else {
153+
// when `useEmberModule` is set, we don't need to do anything here
154+
if (!useEmberModule) {
141155
if (local.name !== 'Ember') {
142156
path.scope.rename(local.name, 'Ember');
143157
}
@@ -202,12 +216,15 @@ module.exports = function (babel) {
202216

203217
removals.push(specifierPath);
204218

219+
// ensure that the Ember global is imported if needed
220+
state.ensureEmberImport();
221+
205222
if (
206223
path.scope.bindings[local.name].referencePaths.find(
207224
(rp) => rp.parent.type === 'ExportSpecifier'
208225
)
209226
) {
210-
// not safe to use path.scope.rename directly
227+
// not safe to use path.scope.rename directly when this identifier is being directly re-exported
211228
declarations.push(
212229
t.variableDeclaration('var', [
213230
t.variableDeclarator(
@@ -249,7 +266,10 @@ module.exports = function (babel) {
249266
// Replace the occurrences of the imported name with the global name.
250267
referencePaths.forEach((referencePath) => {
251268
if (!isTypescriptNode(referencePath.parentPath)) {
252-
const memberExpression = getMemberExpressionFor(global);
269+
const memberExpression = getMemberExpressionFor(
270+
global,
271+
state.emberIdentifier
272+
);
253273

254274
try {
255275
referencePath.replaceWith(memberExpression);
@@ -373,5 +393,3 @@ module.exports = function (babel) {
373393
// Provide the path to the package's base directory for caching with broccoli
374394
// Ref: https://github.com/babel/broccoli-babel-transpiler#caching
375395
module.exports.baseDir = () => path.resolve(__dirname, '..');
376-
377-
module.exports.uniqueishGlobalName = EmberGlobalImportName;

0 commit comments

Comments
 (0)