Skip to content

Commit 51a9b4b

Browse files
author
Robert Jackson
authored
Merge pull request #175 from NullVoxPopuli/add-option-to-preserve-ember-import
2 parents afdd503 + 61b457e commit 51a9b4b

2 files changed

Lines changed: 108 additions & 8 deletions

File tree

__tests__/index-test.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,52 @@ describe('options', () => {
241241
expect(actual).toEqual(`var _x = Ember.assert;var _y = Ember.inspect;`);
242242
});
243243
});
244+
245+
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+
253+
it(`adds the ember import when used in sub-modules`, () => {
254+
let input = `import Component from '@ember/component';export default class extends Component {}`;
255+
let actual = transform(input, [[Plugin, { useEmberModule: true }]]);
256+
let expected = `import _Ember from 'ember';\nexport default class extends _Ember.Component {}`;
257+
258+
expect(actual).toEqual(expected);
259+
});
260+
261+
it(`keeps the ember import`, () => {
262+
let input = `import Ember from 'ember';let x = Ember;`;
263+
let actual = transform(input, [[Plugin, { useEmberModule: true }]]);
264+
265+
expect(actual).toEqual(input);
266+
});
267+
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+
276+
it(`keeps the ember import when renamed`, () => {
277+
let input = `import BestFramework from 'ember';let x = BestFramework;`;
278+
let actual = transform(input, [[Plugin, { useEmberModule: true }]]);
279+
280+
expect(actual).toEqual(input);
281+
});
282+
283+
it(`import then export`, () => {
284+
let input = `import mbr from 'ember';export const Ember = mbr;`;
285+
let actual = transform(input, [[Plugin, { useEmberModule: true }]]);
286+
287+
expect(actual).toEqual(input);
288+
});
289+
});
244290
});
245291

246292
describe(`import from 'ember'`, () => {

src/index.js

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,17 @@ module.exports = function (babel) {
5858
reverseMapping[importRoot][importName] = imported;
5959
});
6060

61-
function getMemberExpressionFor(global) {
61+
function getMemberExpressionFor(global, emberIdentifier) {
6262
let parts = global.split('.');
6363

6464
let object = parts.shift();
6565
let property = parts.shift();
6666

67+
let objectIdentifier =
68+
object === 'Ember' ? emberIdentifier : t.identifier(object);
69+
6770
let memberExpression = t.MemberExpression(
68-
t.identifier(object),
71+
objectIdentifier,
6972
t.identifier(property)
7073
);
7174

@@ -84,8 +87,49 @@ module.exports = function (babel) {
8487
return {
8588
name: 'ember-modules-api-polyfill',
8689
visitor: {
90+
Program(path, state) {
91+
let options = state.opts || {};
92+
let useEmberModule = Boolean(options.useEmberModule);
93+
94+
let preexistingEmberImportDeclaration = path
95+
.get('body')
96+
.filter((n) => n.type === 'ImportDeclaration')
97+
.find((n) => n.get('source').get('value').node === 'ember');
98+
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;
107+
}
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+
};
127+
},
128+
87129
ImportDeclaration(path, state) {
88-
let ignore = (state.opts && state.opts.ignore) || [];
130+
let options = state.opts || {};
131+
let ignore = options.ignore || [];
132+
let useEmberModule = Boolean(options.useEmberModule);
89133
let node = path.node;
90134
let declarations = [];
91135
let removals = [];
@@ -105,10 +149,14 @@ module.exports = function (babel) {
105149

106150
if (specifierPath) {
107151
let local = specifierPath.node.local;
108-
if (local.name !== 'Ember') {
109-
path.scope.rename(local.name, 'Ember');
152+
153+
// when `useEmberModule` is set, we don't need to do anything here
154+
if (!useEmberModule) {
155+
if (local.name !== 'Ember') {
156+
path.scope.rename(local.name, 'Ember');
157+
}
158+
removals.push(specifierPath);
110159
}
111-
removals.push(specifierPath);
112160
} else {
113161
// import 'ember';
114162
path.remove();
@@ -168,12 +216,15 @@ module.exports = function (babel) {
168216

169217
removals.push(specifierPath);
170218

219+
// ensure that the Ember global is imported if needed
220+
state.ensureEmberImport();
221+
171222
if (
172223
path.scope.bindings[local.name].referencePaths.find(
173224
(rp) => rp.parent.type === 'ExportSpecifier'
174225
)
175226
) {
176-
// 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
177228
declarations.push(
178229
t.variableDeclaration('var', [
179230
t.variableDeclarator(
@@ -215,7 +266,10 @@ module.exports = function (babel) {
215266
// Replace the occurrences of the imported name with the global name.
216267
referencePaths.forEach((referencePath) => {
217268
if (!isTypescriptNode(referencePath.parentPath)) {
218-
const memberExpression = getMemberExpressionFor(global);
269+
const memberExpression = getMemberExpressionFor(
270+
global,
271+
state.emberIdentifier
272+
);
219273

220274
try {
221275
referencePath.replaceWith(memberExpression);

0 commit comments

Comments
 (0)