Skip to content

Commit

Permalink
Merge pull request #1042 from embroider-build/optimize-ember-cli-babel2
Browse files Browse the repository at this point in the history
Fix ember-cli-babel optimization
  • Loading branch information
ef4 committed Dec 8, 2021
2 parents 28656a5 + 15e8e52 commit d0fddf9
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 18 deletions.
9 changes: 8 additions & 1 deletion packages/compat/src/build-compat-addon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ function buildCompatAddon(originalPackage: Package, v1Cache: V1InstanceCache): N

let oldPackages = v1Cache.getAddons(originalPackage.root);

if (oldPackages.length > 1) {
// extensibility hook that allows a compat adapter to optimize its own
// smooshing. We do it early so that if it reduces all the way to zero, the
// next check will handle that.
oldPackages = oldPackages[0].reduceInstances(oldPackages);
}

if (oldPackages.length === 0) {
// this happens when the v1 addon wasn't actually getting instantiated at
// all, which can happen if the app uses `addons.blacklist` or another addon
Expand All @@ -39,7 +46,7 @@ function buildCompatAddon(originalPackage: Package, v1Cache: V1InstanceCache): N
return new EmptyPackageTree(originalPackage.name);
}

let needsSmooshing = oldPackages[0].hasAnyTrees();
let needsSmooshing = oldPackages.length > 1 && oldPackages[0].hasAnyTrees();
if (needsSmooshing) {
let trees = oldPackages.map(pkg => pkg.v2Tree).reverse();
let smoosher = new SmooshPackageJSON(trees, { annotation: originalPackage.name });
Expand Down
30 changes: 13 additions & 17 deletions packages/compat/src/compat-adapters/ember-cli-babel.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
import writeFile from 'broccoli-file-creator';
import V1Addon from '../v1-addon';

// Because almost every addon depends on ember-cli-babel, and because ember-cli
// instantiates a separate instance of Addon per consumer, approximately *half*
// of all Addon instances in a typical app will be copies of ember-cli-babel.
//
// Under embroider, *all* of them should be contributing no files to the build.
export default class EmberCliBabel extends V1Addon {
// this ensures we don't bother smooshing together a large number of useless
// copies of the addon.
hasAnyTrees() {
return false;
}

// and the one copy that we do emit should just be an empty valid package. We
// don't want the babel helpers it emits, they're not even used under
// Embroider anyway.
get v2Tree() {
return writeFile('package.json', JSON.stringify(this.newPackageJSON, null, 2));
// the only copy of ember-cli-babel that might need to do something is the
// first one that wants to emit babel polyfills. No other copy is allowed to
// emit anything into the build.
reduceInstances(copies: EmberCliBabel[]): EmberCliBabel[] {
let polyfillCopy = copies.find(c => {
let instance = c.addonInstance as any;
return typeof instance._shouldIncludePolyfill === 'function' && instance._shouldIncludePolyfill();
});
if (polyfillCopy) {
return [polyfillCopy];
} else {
return [];
}
}
}
20 changes: 20 additions & 0 deletions packages/compat/src/v1-addon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,26 @@ export default class V1Addon {
}
}

// Optional extensible hook for pruning down the list of redundant addon
// instances produces by the classic ember-cli architecture. ember-cli
// instantiates each addon *per consumer*, not per package. So a given package
// will have many addon instances, and Embroider dutifully produces a V1Addon
// instance for each one, and then needs to mimic the classic smooshing
// behavior between them.
//
// But some packages (and ember-cli-babel is the motivating example) produce a
// huge number of instances that do nothing useful and incur significant cost.
// This hook allows their compat adapter to prune down the set, using
// addon-specific knowledge of which instance(s) are actually important.
//
// The order of the instances is significant. The first one is the one with
// the highest precedence, meaning its files would win under classic
// smooshing.
reduceInstances(instances: V1Addon[]): V1Addon[] {
// the default beahvior is that all copies matter
return instances;
}

// this is only defined when there are custom AST transforms that need it
@Memoize()
private get templateCompiler(): NodeTemplateCompiler | undefined {
Expand Down

0 comments on commit d0fddf9

Please sign in to comment.