Skip to content

Commit

Permalink
Merge pull request #642 from embroider-build/renamed-modules-support
Browse files Browse the repository at this point in the history
Renamed modules support
  • Loading branch information
ef4 authored Oct 22, 2024
2 parents 2a2d239 + 11dc23b commit ce42c05
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 60 deletions.
73 changes: 59 additions & 14 deletions packages/ember-auto-import/ts/auto-import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Splitter from './splitter';
import { Bundler, debugBundler } from './bundler';
import Analyzer from './analyzer';
import type { TreeType } from './analyzer';
import Package from './package';
import Package, { V2AddonResolver } from './package';
import BroccoliDebug from 'broccoli-debug';
import BundleConfig from './bundle-config';
import type { Node } from 'broccoli-node-api';
Expand All @@ -12,6 +12,7 @@ import {
AppInstance,
findTopmostAddon,
isDeepAddonInstance,
PackageCache,
} from '@embroider/shared-internals';
import WebpackBundler from './webpack';
import { Memoize } from 'typescript-memoize';
Expand Down Expand Up @@ -49,6 +50,7 @@ export interface AutoImportSharedAPI {

export default class AutoImport implements AutoImportSharedAPI {
private packages: Set<Package> = new Set();
private packageCache: PackageCache;
private env: 'development' | 'test' | 'production';
private consoleWrite: (msg: string) => void;
private analyzers: Map<Analyzer, Package> = new Map();
Expand All @@ -67,7 +69,13 @@ export default class AutoImport implements AutoImportSharedAPI {

constructor(addonInstance: AddonInstance) {
let topmostAddon = findTopmostAddon(addonInstance);
this.packages.add(Package.lookupParentOf(topmostAddon));
this.packageCache = PackageCache.shared(
'ember-auto-import',
topmostAddon.project.root
);
this.packages.add(
Package.lookupParentOf(topmostAddon, this.v2AddonResolver)
);
let host = topmostAddon.app;

this.installAppFilter(host);
Expand Down Expand Up @@ -108,7 +116,7 @@ export default class AutoImport implements AutoImportSharedAPI {
treeType?: TreeType,
supportsFastAnalyzer?: true
) {
let pack = Package.lookupParentOf(addon);
let pack = Package.lookupParentOf(addon, this.v2AddonResolver);
this.packages.add(pack);
let analyzer = new Analyzer(
debugTree(tree, `preprocessor:input-${this.analyzers.size}`),
Expand All @@ -124,17 +132,55 @@ export default class AutoImport implements AutoImportSharedAPI {
this.v2Addons.set(packageName, packageRoot);
}

private makeBundler(allAppTree: Node): Bundler {
// this is a concession to compatibility with ember-cli's treeForApp
// merging. Addons are allowed to inject modules into the app, and it's
// extremely common that those modules want to import from the addons
// themselves, even though this jumps arbitrarily many levels in the
// dependency graph.
//
// Since we handle v2 addons, we need to make sure all v2 addons function as
// "dependencies" of the app even though they're not really.
this.rootPackage.magicDeps = this.v2Addons;
get v2AddonResolver(): V2AddonResolver {
return {
hasV2Addon: (name: string): boolean => {
return this.v2Addons.has(name);
},

v2AddonRoot: (name: string): string | undefined => {
return this.v2Addons.get(name);
},

handleRenaming: (name: string): string => {
let hit = this.renamedModules().get(name);
if (hit) {
return hit;
}
hit = this.renamedModules().get(name + '.js');
if (hit) {
return hit;
}
hit = this.renamedModules().get(name + '/index.js');
if (hit) {
return hit;
}
return name;
},
};
}

private _renamedModules: Map<string, string> | undefined;

private renamedModules(): Map<string, string> {
if (!this._renamedModules) {
this._renamedModules = new Map();
for (let packageRoot of this.v2Addons.values()) {
let pkg = this.packageCache.get(packageRoot);
if (pkg.isV2Addon()) {
let renamedModules = pkg.meta['renamed-modules'];
if (renamedModules) {
for (let [from, to] of Object.entries(renamedModules)) {
this._renamedModules.set(from, to);
}
}
}
}
}
return this._renamedModules;
}

private makeBundler(allAppTree: Node): Bundler {
// The Splitter takes the set of imports from the Analyzer and
// decides which ones to include in which bundles
let splitter = new Splitter({
Expand Down Expand Up @@ -166,7 +212,6 @@ export default class AutoImport implements AutoImportSharedAPI {
consoleWrite: this.consoleWrite,
bundles: this.bundles,
webpack,
v2Addons: this.v2Addons,
rootPackage: this.rootPackage,
});
}
Expand Down
1 change: 0 additions & 1 deletion packages/ember-auto-import/ts/bundler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export interface BundlerOptions {
packages: Set<Package>;
bundles: BundleConfig;
webpack: typeof webpack;
v2Addons: Map<string, string>;
rootPackage: Package;
}

Expand Down
37 changes: 21 additions & 16 deletions packages/ember-auto-import/ts/package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ export interface Options {

export interface DepResolution {
type: 'package';
path: string;
packageName: string;
packageRoot: string;
resolvedSpecifier: string;
}

interface LocalResolution {
Expand All @@ -71,6 +71,12 @@ type Resolution =
| URLResolution
| ImpreciseResolution;

export type V2AddonResolver = {
hasV2Addon(name: string): boolean;
v2AddonRoot(name: string): string | undefined;
handleRenaming(name: string): string;
};

export default class Package {
public name: string;
public root: string;
Expand All @@ -87,21 +93,26 @@ export default class Package {
private pkgGeneration: number;
private pkgCache: any;
private macrosConfig: MacrosConfig | undefined;
private extraResolve: V2AddonResolver;

static lookupParentOf(child: AddonInstance): Package {
static lookupParentOf(
child: AddonInstance,
extraResolve: V2AddonResolver
): Package {
if (!parentCache.has(child)) {
let pkg = packageCache.get(child.parent);
if (!pkg) {
pkg = new this(child);
pkg = new this(child, extraResolve);
packageCache.set(child.parent, pkg);
}
parentCache.set(child, pkg);
}
return parentCache.get(child)!;
}

constructor(child: AddonInstance) {
constructor(child: AddonInstance, extraResolve: V2AddonResolver) {
this.name = child.parent.pkg.name;
this.extraResolve = extraResolve;

if (isDeepAddonInstance(child)) {
this.root = this.pkgRoot = child.parent.root;
Expand Down Expand Up @@ -233,19 +244,13 @@ export default class Package {
return `${this.name}/${this.isAddon ? 'addon' : 'app'}`;
}

// extra dependencies that must be treated as if they were really dependencies
// of this package. sigh.
//
// maps from packageName to packageRoot
magicDeps: Map<string, string> | undefined;

hasDependency(name: string): boolean {
let { pkg } = this;
return Boolean(
pkg.dependencies?.[name] ||
pkg.devDependencies?.[name] ||
pkg.peerDependencies?.[name] ||
this.magicDeps?.get(name)
this.extraResolve.hasV2Addon(name)
);
}

Expand All @@ -269,7 +274,7 @@ export default class Package {
return Boolean(
pkg.dependencies?.[name] ||
pkg.peerDependencies?.[name] ||
this.magicDeps?.has(name)
this.extraResolve.hasV2Addon(name)
);
}

Expand Down Expand Up @@ -319,7 +324,7 @@ export default class Package {
break;
}

let path = this.aliasFor(importedPath);
let path = this.extraResolve.handleRenaming(this.aliasFor(importedPath));
let packageName = getPackageName(path);
if (!packageName) {
// this can only happen if the user supplied an alias that points at a
Expand All @@ -344,9 +349,9 @@ export default class Package {
) {
return {
type: 'package',
path: localPath,
packageName: this.name,
packageRoot: join(this.root, 'app'),
resolvedSpecifier: path,
};
}
}
Expand All @@ -368,7 +373,7 @@ export default class Package {
}

if (!packageRoot) {
packageRoot = this.magicDeps?.get(packageName);
packageRoot = this.extraResolve.v2AddonRoot(packageName);
}

if (packageRoot == null) {
Expand All @@ -384,9 +389,9 @@ export default class Package {
this.assertAllowedDependency(packageName, fromPath);
return {
type: 'package',
path,
packageName,
packageRoot,
resolvedSpecifier: path,
};
}

Expand Down
15 changes: 10 additions & 5 deletions packages/ember-auto-import/ts/splitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import { satisfies } from 'semver';
const debug = makeDebug('ember-auto-import:splitter');

export interface ResolvedImport {
specifier: string;
requestedSpecifier: string;
resolvedSpecifier: string;
packageName: string;
packageRoot: string;
importedBy: LiteralImport[];
Expand Down Expand Up @@ -115,7 +116,8 @@ export default class Splitter {
seenAlready.importedBy.push(imp);
} else {
targets.set(imp.specifier, {
specifier: imp.specifier,
requestedSpecifier: imp.specifier,
resolvedSpecifier: target.resolvedSpecifier,
packageName: target.packageName,
packageRoot: target.packageRoot,
importedBy: [imp],
Expand Down Expand Up @@ -284,9 +286,11 @@ export default class Splitter {
}

private sortBundle(bundle: BundleDependencies) {
bundle.staticImports.sort((a, b) => a.specifier.localeCompare(b.specifier));
bundle.staticImports.sort((a, b) =>
a.requestedSpecifier.localeCompare(b.requestedSpecifier)
);
bundle.dynamicImports.sort((a, b) =>
a.specifier.localeCompare(b.specifier)
a.requestedSpecifier.localeCompare(b.requestedSpecifier)
);
bundle.dynamicTemplateImports.sort((a, b) =>
a.cookedQuasis[0].localeCompare(b.cookedQuasis[0])
Expand Down Expand Up @@ -329,7 +333,8 @@ class LazyPrintDeps {

private describeResolvedImport(imp: ResolvedImport) {
return {
specifier: imp.specifier,
requestedSpecifier: imp.requestedSpecifier,
resolvedSpecifier: imp.resolvedSpecifier,
packageRoot: imp.packageRoot,
importedBy: imp.importedBy.map(this.describeImport.bind(this)),
};
Expand Down
Loading

0 comments on commit ce42c05

Please sign in to comment.