Skip to content

Commit

Permalink
Add polyfill that allows apps and addons to use @ember-data re… (#318)
Browse files Browse the repository at this point in the history
Add polyfill that allows apps and addons to use `@ember-data` related packages regardless of `ember-data` version
  • Loading branch information
rwjblue committed Jan 30, 2020
2 parents f305de0 + 334e0ee commit 2ba3ef7
Show file tree
Hide file tree
Showing 5 changed files with 344 additions and 27 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ interface EmberCLIBabelConfig {
disableDebugTooling?: boolean;
disablePresetEnv?: boolean;
disableEmberModulesAPIPolyfill?: boolean;
disableEmberDataPackagesPolyfill?: boolean;
disableDecoratorTransforms?: boolean;
extensions?: string[];
};
Expand Down
25 changes: 25 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ module.exports = {
userPlugins,
this._getDebugMacroPlugins(config),
this._getEmberModulesAPIPolyfill(config),
this._getEmberDataPackagesPolyfill(config),
shouldCompileModules && this._getModulesPlugin(),
userPostTransformPlugins
).filter(Boolean);
Expand Down Expand Up @@ -444,6 +445,16 @@ module.exports = {
}
},

_getEmberDataPackagesPolyfill(config) {
let addonOptions = config['ember-cli-babel'] || {};

if (addonOptions.disableEmberDataPackagesPolyfill) { return; }

if (this._emberDataVersionRequiresPackagesPolyfill()) {
return [[require.resolve('babel-plugin-ember-data-packages-polyfill')]];
}
},

_getPresetEnv(config) {
let options = config.options;

Expand Down Expand Up @@ -511,6 +522,20 @@ module.exports = {
return true;
},

_emberDataVersionRequiresPackagesPolyfill() {
let checker = new VersionChecker(this.project);
let dep = checker.for('ember-data');
let hasEmberData = dep.exists();

if (hasEmberData) {
if (!dep.version) {
throw new Error('EmberData missing version');
}
return semver.lt(dep.version, '3.12.0-alpha.0');
}
return false;
},

_getEmberModulesAPIBlacklist() {
const blacklist = {
'@ember/debug': ['assert', 'deprecate', 'warn'],
Expand Down
182 changes: 165 additions & 17 deletions node-tests/addon-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,24 @@ const CoreObject = require('core-object');
const AddonMixin = require('../index');
const CommonTags = require('common-tags');
const stripIndent = CommonTags.stripIndent;
const FixturifyProject = require('fixturify-project');
const EmberProject = require('ember-cli/lib/models/project');
const MockCLI = require('ember-cli/tests/helpers/mock-cli');
const BroccoliTestHelper = require('broccoli-test-helper');
const createBuilder = BroccoliTestHelper.createBuilder;
const createTempDir = BroccoliTestHelper.createTempDir;
const terminateWorkerPool = require('./utils/terminate-workers');
const path = require('path');
const fs = require('fs');
const rimraf = require('rimraf');

function prepareAddon(addon) {
addon.pkg.keywords.push('ember-addon');
addon.pkg['ember-addon'] = {};
addon.files['index.js'] = 'module.exports = { name: require("./package").name };';

return addon;
}

let Addon = CoreObject.extend(AddonMixin);

Expand All @@ -22,6 +36,8 @@ describe('ember-cli-babel', function() {
beforeEach(function() {
this.ui = new MockUI();
let project = {
isEmberCLIProject: () => true,
_addonsInitialized: true,
root: __dirname,
emberCLIVersion: () => '2.16.2',
dependencies() { return {}; },
Expand Down Expand Up @@ -985,19 +1001,19 @@ describe('ember-cli-babel', function() {
});

it('provides an annotation including parent name - addon', function() {
this.addon.parent = {
this.addon.parent = Object.assign({}, this.addon.parent, {
name: 'derpy-herpy',
dependencies() { return {}; },
};
});
let result = this.addon.buildBabelOptions();
expect(result.annotation).to.include('derpy-herpy');
});

it('provides an annotation including parent name - project', function() {
this.addon.parent = {
name() { return 'derpy-herpy'; },
this.addon.parent = Object.assign({}, this.addon.parent, {
name: 'derpy-herpy',
dependencies() { return {}; },
};
});
let result = this.addon.buildBabelOptions();
expect(result.annotation).to.include('derpy-herpy');
});
Expand Down Expand Up @@ -1044,27 +1060,27 @@ describe('ember-cli-babel', function() {

it('does not include all provided options', function() {
let babelOptions = { blah: true };
this.addon.parent = {
this.addon.parent = Object.assign({}, this.addon.parent, {
dependencies() { return {}; },
options: {
babel: babelOptions,
},
};
});

let result = this.addon.buildBabelOptions();
expect(result.blah).to.be.undefined;
});

it('includes user plugins in parent.options.babel.plugins', function() {
let plugin = {};
this.addon.parent = {
this.addon.parent = Object.assign({}, this.addon.parent, {
dependencies() { return {}; },
options: {
babel: {
plugins: [ plugin ]
},
},
};
});

let result = this.addon.buildBabelOptions();
expect(result.plugins).to.deep.include(plugin);
Expand All @@ -1073,15 +1089,15 @@ describe('ember-cli-babel', function() {
it('includes postTransformPlugins after preset-env plugins', function() {
let plugin = {};
let pluginAfter = {};
this.addon.parent = {
this.addon.parent = Object.assign({}, this.addon.parent, {
dependencies() { return {}; },
options: {
babel: {
plugins: [ plugin ],
postTransformPlugins: [ pluginAfter ]
},
},
};
});

let result = this.addon.buildBabelOptions();

Expand All @@ -1096,29 +1112,29 @@ describe('ember-cli-babel', function() {
disablePresetEnv: true,
}
};
this.addon.parent = {
this.addon.parent = Object.assign({}, this.addon.parent, {
dependencies() { return {}; },
options: {
babel6: {
plugins: [ {} ]
},
},
};
});

let result = this.addon.buildBabelOptions(options);
expect(result.presets).to.deep.equal([]);
});

it('user plugins are before preset-env plugins', function() {
let plugin = function Plugin() {};
this.addon.parent = {
this.addon.parent = Object.assign({}, this.addon.parent, {
dependencies() { return {}; },
options: {
babel: {
plugins: [ plugin ]
},
},
};
});

let result = this.addon.buildBabelOptions();
expect(result.plugins[0]).to.equal(plugin);
Expand Down Expand Up @@ -1168,12 +1184,12 @@ describe('ember-cli-babel', function() {

it('passes options.babel through to preset-env', function() {
let babelOptions = { loose: true };
this.addon.parent = {
this.addon.parent = Object.assign({}, this.addon.parent, {
dependencies() { return {}; },
options: {
babel: babelOptions,
},
};
});

let options = this.addon.buildBabelOptions();

Expand Down Expand Up @@ -1214,3 +1230,135 @@ describe('ember-cli-babel', function() {
});
});
});

describe('EmberData Packages Polyfill', function() {
this.timeout(0);

let input;
let output;
let subject;
let setupForVersion;
let project;
let unlink;

beforeEach(function() {
let self = this;
setupForVersion = co.wrap(function*(v) {
let fixturifyProject = new FixturifyProject('whatever', '0.0.1');
fixturifyProject.addDependency('ember-data', v, addon => {
return prepareAddon(addon);
});
fixturifyProject.addDependency('ember-cli-babel', 'babel/ember-cli-babel#master');
fixturifyProject.addDependency('random-addon', '0.0.1', addon => {
return prepareAddon(addon);
});
let pkg = JSON.parse(fixturifyProject.toJSON('package.json'));
fixturifyProject.writeSync();

let linkPath = path.join(fixturifyProject.root, '/whatever/node_modules/ember-cli-babel');
let addonPath = path.resolve(__dirname, '../');
rimraf.sync(linkPath);
fs.symlinkSync(addonPath, linkPath);
unlink = () => {
fs.unlinkSync(linkPath);
};

let cli = new MockCLI();
let root = path.join(fixturifyProject.root, 'whatever');
project = new EmberProject(root, pkg, cli.ui, cli);
project.initializeAddons();

self.addon = project.addons.find(a => { return a.name === 'ember-cli-babel'; });

input = yield createTempDir();
});
});

afterEach(co.wrap(function*() {
unlink();
yield input.dispose();
yield output.dispose();
// shut down workers after the tests are run so that mocha doesn't hang
yield terminateWorkerPool();
}));

it("does not convert when _emberDataVersionRequiresPackagesPolyfill returns false", co.wrap(function*() {
yield setupForVersion('3.12.0-alpha.0');
input.write({
"foo.js": `export { default } from '@ember-data/store';`,
"bar.js": `import Model, { attr } from '@ember-data/model';\nexport var User = Model;\nexport var name = attr;`,
});

subject = this.addon.transpileTree(input.path(), {
'ember-cli-babel': {
compileModules: false,
disableDebugTooling: true,
disableEmberDataPackagesPolyfill: true
}
});

output = createBuilder(subject);

yield output.build();

expect(
output.read()
).to.deep.equal({
"foo.js": `export { default } from '@ember-data/store';`,
"bar.js": `import Model, { attr } from '@ember-data/model';\nexport var User = Model;\nexport var name = attr;`,
});
}));

it("does not convert for EmberData when _emberDataVersionRequiresPackagesPolyfill returns true and disableEmberDataPackagesPolyfill is true", co.wrap(function*() {
yield setupForVersion('3.11.0');
input.write({
"foo.js": `export { default } from '@ember-data/store';`,
"bar.js": `import Model, { attr } from '@ember-data/model';\nexport var User = Model;\nexport var name = attr;`,
});

subject = this.addon.transpileTree(input.path(), {
'ember-cli-babel': {
compileModules: false,
disableDebugTooling: true,
disableEmberDataPackagesPolyfill: true
}
});

output = createBuilder(subject);

yield output.build();

expect(
output.read()
).to.deep.equal({
"foo.js": `export { default } from '@ember-data/store';`,
"bar.js": `import Model, { attr } from '@ember-data/model';\nexport var User = Model;\nexport var name = attr;`,
});
}));

it("it does convert for EmberData when _emberDataVersionRequiresPackagesPolyfill returns true", co.wrap(function*() {
yield setupForVersion('3.11.99');
input.write({
"foo.js": `export { default } from '@ember-data/store';`,
"bar.js": `import Model, { attr } from '@ember-data/model';\nexport var User = Model;export var name = attr;`,
});

subject = this.addon.transpileTree(input.path(), {
'ember-cli-babel': {
compileModules: false,
disableDebugTooling: true,
}
});

output = createBuilder(subject);

yield output.build();

expect(
output.read()
).to.deep.equal({
"foo.js": `import DS from "ember-data";\nexport default DS.Store;`,
"bar.js": `import DS from "ember-data";\nexport var User = DS.Model;\nexport var name = DS.attr;`,
});
}));
});
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"@babel/runtime": "^7.8.3",
"amd-name-resolver": "^1.2.1",
"babel-plugin-debug-macros": "^0.3.0",
"babel-plugin-ember-data-packages-polyfill": "^0.1.0",
"babel-plugin-ember-modules-api-polyfill": "^2.12.0",
"babel-plugin-module-resolver": "^3.1.1",
"broccoli-babel-transpiler": "^7.4.0",
Expand All @@ -58,8 +59,10 @@
"broccoli-source": "^1.1.0",
"clone": "^2.1.2",
"ember-cli-babel-plugin-helpers": "^1.1.0",
"ember-cli-version-checker": "^2.1.2",
"ember-cli-version-checker": "^4.1.0",
"ensure-posix-path": "^1.0.2",
"fixturify-project": "^1.10.0",
"rimraf": "^3.0.1",
"semver": "^5.5.0"
},
"devDependencies": {
Expand Down
Loading

0 comments on commit 2ba3ef7

Please sign in to comment.