From 15e8e52a6b4c37f5cf1604cac7e919bdc94cd908 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Tue, 7 Dec 2021 23:45:32 -0500 Subject: [PATCH] Fix ember-cli-babel optimization --- packages/compat/src/build-compat-addon.ts | 9 +++++- .../src/compat-adapters/ember-cli-babel.ts | 30 ++++++++----------- packages/compat/src/v1-addon.ts | 20 +++++++++++++ 3 files changed, 41 insertions(+), 18 deletions(-) diff --git a/packages/compat/src/build-compat-addon.ts b/packages/compat/src/build-compat-addon.ts index 3ab2fe70a..7305ecaf6 100644 --- a/packages/compat/src/build-compat-addon.ts +++ b/packages/compat/src/build-compat-addon.ts @@ -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 @@ -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 }); diff --git a/packages/compat/src/compat-adapters/ember-cli-babel.ts b/packages/compat/src/compat-adapters/ember-cli-babel.ts index aa3cb43ad..f71105a52 100644 --- a/packages/compat/src/compat-adapters/ember-cli-babel.ts +++ b/packages/compat/src/compat-adapters/ember-cli-babel.ts @@ -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 []; + } } } diff --git a/packages/compat/src/v1-addon.ts b/packages/compat/src/v1-addon.ts index 348cd8199..6e48d0011 100644 --- a/packages/compat/src/v1-addon.ts +++ b/packages/compat/src/v1-addon.ts @@ -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 {